/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */


package alex;


import TablaDeSimbolos.*;
import java.util.ArrayList;
import java.util.Hashtable;


/**
 *
 * @author Administrador
 */
public class AsinTcore {

    private static final int WRITE = 0;
    private static final int WRITELN = 1;

    private static final int READ = 0;
    private static final int READLN = 1;

    private static final int REFERENCIA = 0;
    private static final int VALOR = 1;
    private AleXcore alex;
    private Token ultimoToken;
    private TablaDeSimbolos TdS = new TablaDeSimbolos();
    private MEPaHandler MepaH;




    public class ExcepcionErrorSintactico extends Exception {
        public ExcepcionErrorSintactico(String error){
            super(error);
        }
    }

    public class ExcepcionErrorSemantico extends Exception {
        public ExcepcionErrorSemantico(String error){
            super(error);
        }
    }

    public AsinTcore(String path, MEPaHandler archivoSalida) throws Buffer.ExcepcionArchivoNoExiste{
        alex = new AleXcore(path);
        MepaH = archivoSalida;

    }

    public void analizar() throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        program();
    }


    private void program() throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        
        EncabezadoProgram();
        MepaH.generarInstruccion("INPP");
        TdS.apilarNivel();  
        int etiquetaFINAL = TdS.nuevaEtiqueta();
        registrarFuncionesPredefinidas();
        bloque();
        MepaH.generarInstruccion("DSVS l"+etiquetaFINAL);
        
        declararFuncionesPredefinidas();

        MepaH.generarInstruccion("l"+etiquetaFINAL+" NADA");
        MepaH.generarInstruccion("PARA");
        TdS.desapilarNivel();
        if (!ultimoToken.isToken(Token.PUNTO)){
            throw new ExcepcionErrorSintactico("Se esperaba un '.' en la linea " + ultimoToken.getLinea());
        }
        ultimoToken = alex.getToken();
        if (!ultimoToken.isToken(Token.EOF)){
            throw new ExcepcionErrorSintactico("Error, hay caracteres luego del '.', en la linea " + ultimoToken.getLinea());
        }
    }


    private void EncabezadoProgram() throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico{
        ultimoToken = alex.getToken();
        if (ultimoToken.isToken(Token.PROGRAM)){
            ultimoToken = alex.getToken();
            if ((ultimoToken.isToken(Token.IDENTIFICADOR)) && (alex.esReservada(ultimoToken.getLexema())==Token.IDENTIFICADOR)){
                ultimoToken = alex.getToken();
                if (ultimoToken.isToken(Token.PUNTOCOMA)){
                    // ENCABEZADO BIEN FORMADO.
                } else {
                    throw new ExcepcionErrorSintactico("Se esperaba un ;, en linea: "+ ultimoToken.getLinea());
                }
            } else{
                throw new ExcepcionErrorSintactico("Se esperaba un IDENTIFICADOR, en linea: "+ ultimoToken.getLinea() + ", se obtuvo "+ ultimoToken.getLexema());
            }
        }
        else{
            throw new ExcepcionErrorSintactico("Se esperaba PROGRAM, en linea: "+ ultimoToken.getLinea() + ", se obtuvo "+ ultimoToken.getLexema());
        }
    }


    // ------------------------------- CONSTANTES --------------------------------------------


    private Constante identificadorDeConstante()throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
         Constante Ct = null;
         if (ultimoToken.isToken(Token.IDENTIFICADOR)){
            String identificador = ultimoToken.getLexema();
             ultimoToken = alex.getToken();

            if (TdS.pertenece(identificador)){

                    Elemento entradaDeTabla = TdS.getEntrada(identificador);
                    if(entradaDeTabla.esConstante()){
                            Ct = (Constante) entradaDeTabla;
                    }
                    else {
                     throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador: "+identificador+" no es una constante");
                    }


            }
            else{
                //ERROR(identificador de constante no declarado)
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador: "+identificador+"  no está declarado previamente");
            }
         }
         return Ct;
    }


    private Constante constante(String id, int valor, TipoSimple tipo)throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{

        return restoConstante(id,valor,tipo);
    }


    private Constante restoConstante(String id, int valor, TipoSimple tipo)throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        Constante Ct = null;
        int signo = 1;

        if ((ultimoToken.getLexema().equals("-"))|| (ultimoToken.getLexema().equals("+"))) {
            if(ultimoToken.getLexema().equals("-")){
                signo=-1;
            }
            ultimoToken = alex.getToken();
        }

        if (ultimoToken.isToken(Token.NUMERO)) {
            id = "";
            valor = new Integer(ultimoToken.getLexema()) * signo;
            tipo = new Entero();
            Ct = new Constante(valor, tipo);
            ultimoToken = alex.getToken();

        }

        else if (ultimoToken.isToken(Token.IDENTIFICADOR)){
            Constante Ct2 = identificadorDeConstante();
            Ct = new Constante(Ct2.getValor()*signo, Ct2.getTipo());

        }
        else{
            throw new ExcepcionErrorSintactico("Se esperaba un IDENTIFICADOR o un NUMERO, en linea: "+ ultimoToken.getLinea() + ", se obtuvo "+ ultimoToken.getLexema());
        }
        
        /*
        if ((ultimoToken.getLexema().equals("-"))|| (ultimoToken.getLexema().equals("+"))) {
        //if (ultimoToken.isToken(Token.OPSUMA)) {
            String signo = ultimoToken.getLexema();
            ultimoToken = alex.getToken();
            if (ultimoToken.isToken(Token.NUMERO)){
                id = "";
                valor = new Integer(signo+valor);
                tipo = new Entero();
                Ct = new Constante(valor, tipo);
                ultimoToken = alex.getToken();
            }
            else if(ultimoToken.isToken(Token.IDENTIFICADOR)){
                int v1=0;
                Constante Ct1 = identificadorDeConstante();
                if(Ct1.getTipo().esEntero()){
                    if(signo.equals("-")){
                        Ct = new Constante((Ct1.getValor()*(-1)), (Ct1.getTipo()));


                    } else {valor = v1;}

                }
                else {

                    throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". No se puede aplicar el operador unario "+signo+" sobre un operando no entero");
                }
            }
        }
        else if (ultimoToken.isToken(Token.NUMERO)) {
            id = "";
            valor = new Integer(ultimoToken.getLexema());
            tipo = new Entero();
            Ct = new Constante(valor, tipo);
            ultimoToken = alex.getToken();

        }

        else if (ultimoToken.isToken(Token.IDENTIFICADOR)){
            Ct = identificadorDeConstante();

        }
        else{
            throw new ExcepcionErrorSintactico("Se esperaba un IDENTIFICADOR o un NUMERO, en linea: "+ ultimoToken.getLinea() + ", se obtuvo "+ ultimoToken.getLexema());
        }*/
        return Ct;
    }


    private void parteDefinicionConstante() throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        if (ultimoToken.isToken(Token.CONST)){
            ultimoToken = alex.getToken();
            definicionConstante();
            restoDefinicionConstante();
        }
    }


    private void definicionConstante() throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        if (ultimoToken.isToken(Token.IDENTIFICADOR)){
            String id=ultimoToken.getLexema();
            if (TdS.perteneceNivelActual(ultimoToken.getLexema())){
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador "+id+" se encuentra en uso");
            }
            ultimoToken = alex.getToken();
            if (ultimoToken.isToken(Token.OPIGUAL)){
                ultimoToken = alex.getToken();

                int valor=0;
                TipoSimple tipo = new TipoSimple();

                Constante Ct =constante(id, valor, tipo);

                TdS.getTope().registrarConstante(id,Ct);


                if (ultimoToken.isToken(Token.PUNTOCOMA)){
                    ultimoToken = alex.getToken();
                } // IF PUNTOCOMA
                else{
                    throw new ExcepcionErrorSintactico("Se esperaba un ;, en linea: "+ ultimoToken.getLinea());
                }


            } //IF ASIGNACION
            else{
                throw new ExcepcionErrorSintactico("Se esperaba un =, en linea: "+ ultimoToken.getLinea() + ", se obtuvo "+ ultimoToken.getLexema());
            }
        } // IF IDENTIFICADOR
        else{
            throw new ExcepcionErrorSintactico("Se esperaba un IDENTIFICADOR, en linea: "+ ultimoToken.getLinea() + ", se obtuvo "+ ultimoToken.getLexema());
        }


    }


    private void restoDefinicionConstante() throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        if (ultimoToken.isToken(Token.IDENTIFICADOR)){
            definicionConstante();
            restoDefinicionConstante();
        }
    }






    // ------------------------------- TIPOS --------------------------------------------


    private void parteDefinicionTipos() throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        if (ultimoToken.isToken(Token.TYPE)){
            ultimoToken = alex.getToken();
            definicionTipo();
            restoDefinicionTipo();
        }
    }


    private void definicionTipo() throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        if (ultimoToken.isToken(Token.IDENTIFICADOR)){
            String identificador = ultimoToken.getLexema();

            ultimoToken = alex.getToken();


            if (ultimoToken.isToken(Token.OPIGUAL)){
                ultimoToken = alex.getToken();
                if (!TdS.perteneceNivelActual(identificador)){
                    Tipo T = tipo();

                    if (ultimoToken.isToken(Token.PUNTOCOMA)){
                                // DECLARACION DE TIPO CORRECTA
                    }
                    else { throw new ExcepcionErrorSintactico("Se esperaba ';' en linea " + ultimoToken.getLinea()); }
                    TdS.getTope().registrarTipo(identificador, T);

            }else{
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador "+identificador+" se encuentra en uso");
            }

            } //IF ASIGNACION
            else{
                throw new ExcepcionErrorSintactico("Se esperaba un =, en linea: "+ ultimoToken.getLinea() + ", se obtuvo "+ ultimoToken.getLexema());
            }
        } // IF IDENTIFICADOR
        else{
            throw new ExcepcionErrorSintactico("Se esperaba un IDENTIFICADOR, en linea: "+ ultimoToken.getLinea() + ", se obtuvo "+ ultimoToken.getLexema());
        }


    }



    private void restoDefinicionTipo() throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        ultimoToken = alex.getToken();
        if (ultimoToken.isToken(Token.IDENTIFICADOR)){
            definicionTipo();

            restoDefinicionTipo();
        }
    }


    private  Subrango tipoSubrango()throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        int limiteInf=0;
        int limiteSup=0;
        Subrango subR = null;
        int signo= 1;
        int signo2= 1;

        if (ultimoToken.isToken(Token.OPSUMA)){
          if ((ultimoToken.getLexema().equals("-"))||(ultimoToken.getLexema().equals("+"))){
              if(ultimoToken.getLexema().equals("-")){
                  signo = -1;
              }
              ultimoToken = alex.getToken();

          }else{
                throw new ExcepcionErrorSintactico("Se esperaba una constante numerica en linea: "+ ultimoToken.getLinea() + ", se obtuvo "+ ultimoToken.getLexema());
            }
        }

        if (ultimoToken.isToken(Token.NUMERO) || ultimoToken.isToken(Token.IDENTIFICADOR)){
            if(ultimoToken.isToken(Token.IDENTIFICADOR)){
                String identificador = ultimoToken.getLexema();
                Elemento entradaDeTabla=null;
                if(TdS.pertenece(identificador)){
                entradaDeTabla = TdS.getEntrada(identificador);
                }
                else {
                    throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador: "+identificador+" no esta definido");
                }
                if(entradaDeTabla.esConstante()){
                    Constante Ct = (Constante) entradaDeTabla;
                    if (Ct.getTipo().esEntero()){
                        limiteInf = Ct.getValor() * signo;
                    }else {throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador: "+identificador+" no es una constante numérica");}


                 }
                else {
                    throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador: "+identificador+" no es una constante");
                }
            } else {
                limiteInf = Integer.valueOf(ultimoToken.getLexema()) * signo;
            }
            ultimoToken = alex.getToken();
            if (ultimoToken.isToken(Token.PUNTOPUNTO)){
                ultimoToken = alex.getToken();
                if ((ultimoToken.getLexema().equals("-"))||(ultimoToken.getLexema().equals("+"))){
                  if(ultimoToken.getLexema().equals("-")){
                      signo2 = -1;
                  }
                  ultimoToken = alex.getToken();
              }
                if (ultimoToken.isToken(Token.NUMERO) || ultimoToken.isToken(Token.IDENTIFICADOR)){
                    if(ultimoToken.isToken(Token.IDENTIFICADOR)){
                        String identificador = ultimoToken.getLexema();
                        Elemento entradaDeTabla=null;
                        if(TdS.pertenece(identificador)){
                        entradaDeTabla = TdS.getEntrada(identificador);
                        }
                        else {
                            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador: "+identificador+" no esta definido");
                        }
                        if(entradaDeTabla.esConstante()){
                            Constante Ct = (Constante) entradaDeTabla;
                            if (Ct.getTipo().esEntero()){
                                limiteSup = Ct.getValor() * signo2;
                            }else {throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador: "+identificador+" no es una constante numérica");}
                        }
                        else {
                            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador: "+identificador+" no es una constante");
                        }
                    } else {
                        limiteSup = Integer.valueOf(ultimoToken.getLexema()) * signo2;
                        }


                }else  {throw new ExcepcionErrorSintactico("Se esperaba una CONSTANTE, en linea: "+ ultimoToken.getLinea());}
            }  else {throw new ExcepcionErrorSintactico("Se esperaba '..', en linea: "+ ultimoToken.getLinea());}



        }
        
        if (limiteInf<=limiteSup){
            subR= new Subrango(limiteInf, limiteSup);
        }else {throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El limite inferior es mayor al limite superior del subrango");}

        return subR;
    }


    private Tipo tipoSimple()throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        Tipo TS = null;
        Elemento entradaDeTabla;
        
        if ((ultimoToken.isToken(Token.NUMERO)) || (ultimoToken.isToken(Token.OPSUMA))){
            TS = tipoSubrango();
        }
        else if(ultimoToken.isToken(Token.IDENTIFICADOR)){
            String identificador_tipo = ultimoToken.getLexema();

            if (!TdS.pertenece(identificador_tipo)){
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador "+identificador_tipo+" no se encuentra declarado");
            }

            entradaDeTabla = TdS.getEntrada(identificador_tipo);
            
            if(entradaDeTabla.esConstante()){
                TS = tipoSubrango();
            }
            else if(entradaDeTabla.esTipo()){
                TS = (Tipo)entradaDeTabla;
            }
            else{
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador: "+identificador_tipo+" no es un identificador de tipo");
            }
        }

        ultimoToken = alex.getToken();
        return TS;
    }

    private void tipoSimple(Tipo t) throws ExcepcionErrorSemantico{
        if (!TdS.compatibles(t, new Entero()) && !TdS.compatibles(t, new Booleano())){
            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador no es un identificador de tipo simple");
        }
    }


    private Arreglo tipoEstructurado()throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        Arreglo Arr = null;
        if (ultimoToken.isToken(Token.ARRAY)){
            ultimoToken = alex.getToken();
            if(ultimoToken.isToken(Token.CORABRE)){
                ultimoToken = alex.getToken();
                Tipo TS = tipoSimple();
                if (TS.esSubrango()){
                    if(ultimoToken.isToken(Token.CORCIERRA)){
                        ultimoToken = alex.getToken();
                        if(ultimoToken.isToken(Token.OF)){
                            ultimoToken = alex.getToken();
                            Tipo TS2 = tipoSimple();
                            Subrango SubR = (Subrango) TS;
                            
                            if (TS2.esBoolean()){
                                Arr = new Arreglo(SubR.getli(), SubR.getls(), new T_boolean(),(TipoSimple)TS);
                            }
                            else if(TS2.esSubrango()){
                                
                                Arr = new Arreglo(SubR.getli(), SubR.getls(),(TipoSimple)TS2,(TipoSimple) TS);
                            }
                            else{
                                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El tipo de los items del arreglo debe ser un tipo simple");
                            }




                        }else { throw new ExcepcionErrorSintactico("Se esperaba OF, en linea: "+ ultimoToken.getLinea());}
                    } else { throw new ExcepcionErrorSintactico("Se esperaba ']', en linea: "+ ultimoToken.getLinea());}
                }else {throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El indice del arreglo debe ser un subrango");}
            }else { throw new ExcepcionErrorSintactico("Se esperaba '[', en linea: "+ ultimoToken.getLinea());}
        }
        return Arr;
    }




    private Tipo tipo() throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        Tipo T =null;
        if (ultimoToken.isToken(Token.NUMERO) || (ultimoToken.isToken(Token.IDENTIFICADOR))|| (ultimoToken.isToken(Token.OPSUMA))) {

            if(ultimoToken.isToken(Token.IDENTIFICADOR)){
                if (!TdS.pertenece(ultimoToken.getLexema())){
                    throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador "+ultimoToken.getLexema()+" no se encuentra declarado");
                }
            }

            T = tipoSimple();



        }
        else if (ultimoToken.isToken(Token.ARRAY)){
            T = tipoEstructurado();
        }
        else
        {
            throw new ExcepcionErrorSintactico("Se esperaba una declaración de tipo valida, en linea: "+ ultimoToken.getLinea() + ", se obtuvo "+ ultimoToken.getLexema());
        }

        return T;



   }
    // ------------------------------- VARIABLES  --------------------------------------------


    // devuelve la cantidad de memoria utilizada por las definiciones de variables locales
    private int parteDefinicionVariable() throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        int primerlibreoffset=0;
        Hashtable HT;
        int cantMemoria2;


        if (ultimoToken.isToken(Token.VAR)){
            ultimoToken = alex.getToken();

            HT = declaracionVariable(0);
            //heredar primerlibreoffset


            primerlibreoffset = (Integer)HT.get("PRIMERLIBREOFFSET");
            cantMemoria2 = restoDefinicionVariables(primerlibreoffset);

            return cantMemoria2 + (Integer)HT.get("CANTMEMORIA");
        }

        // no hay variables locales
        return 0;
    }




    private Hashtable declaracionVariable(int offset) throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        Hashtable HT, HTResultado;

        if (ultimoToken.isToken(Token.IDENTIFICADOR)){
            String identificador = ultimoToken.getLexema();
            ultimoToken = alex.getToken();


            HT = restoDeclaracionVariable(offset,1);


            if (TdS.perteneceNivelActual(identificador)){
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador "+identificador+" se encuentra en uso");
            }
            else if(TdS.getTope().getIdentificador().equals(identificador.toUpperCase())){
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador "+identificador+" es el identificador de funcion/procedimiento y no puede declararse como variable");
            }
            else{
                //registro la variable
                TdS.getTope().registrarVariable(identificador, TdS.getNivelLexico(),(Integer) HT.get("SIGUIENTEOFFSET"), (Tipo) HT.get("TIPO"));
                MepaH.generarInstruccion("RMEM "+ ((Tipo)HT.get("TIPO")).getCantElem());
            }
        } // IF IDENTIFICADOR
        else{
            throw new ExcepcionErrorSintactico("Se esperaba un IDENTIFICADOR, en linea: "+ ultimoToken.getLinea() + ", se obtuvo "+ ultimoToken.getLexema());
        }


        HTResultado = new Hashtable();
        HTResultado.put("CANTMEMORIA", HT.get("CANTMEMORIA"));
        HTResultado.put("PRIMERLIBREOFFSET", HT.get("PRIMERLIBREOFFSET"));
        int siguienteoffset = (Integer) HT.get("SIGUIENTEOFFSET");
        HTResultado.put("SIGUIENTEOFFSET", siguienteoffset);

        return HTResultado;
    }


    // devuelve primer libre offset
    // parametros: siguienteoffset, primerlibreoffset, tipo, cantmemoria
    private Hashtable restoDeclaracionVariable(int offset, int cantidad) throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        Hashtable atribSintetizados;
        Hashtable atribSintetizados2 = new Hashtable();
        Tipo T;
        int primerLibreOffset, siguienteoffset, cantMemoria;
 

        if (ultimoToken.isToken(Token.DOSPUNTOS)){
          
            ultimoToken = alex.getToken();
            T = tipo();

            cantMemoria = T.getCantElem() * cantidad;
            primerLibreOffset = offset + cantMemoria;
            siguienteoffset = offset + cantMemoria - T.getCantElem() ;

            atribSintetizados2.put("CANTMEMORIA", cantMemoria);
            atribSintetizados2.put("TIPO", T);
            atribSintetizados2.put("PRIMERLIBREOFFSET", primerLibreOffset);
            atribSintetizados2.put("SIGUIENTEOFFSET", siguienteoffset);


        } //IF ASIGNACION
        else if (ultimoToken.isToken(Token.COMA)){
            ultimoToken = alex.getToken();
            String identificador = ultimoToken.getLexema();


            ultimoToken = alex.getToken();


            atribSintetizados = restoDeclaracionVariable(offset, cantidad+1);


            if(TdS.perteneceNivelActual(identificador)){
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador "+identificador+" se encuentra en uso");
            }
            else {
                TdS.getTope().registrarVariable(identificador, TdS.getNivelLexico(),(Integer) atribSintetizados.get("SIGUIENTEOFFSET"), (Tipo) atribSintetizados.get("TIPO"));


                primerLibreOffset = (Integer) atribSintetizados.get("PRIMERLIBREOFFSET");
                cantMemoria = (Integer)atribSintetizados.get("CANTMEMORIA");
                siguienteoffset = (Integer) atribSintetizados.get("SIGUIENTEOFFSET") - ((Tipo) atribSintetizados.get("TIPO")).getCantElem();
                T = (Tipo)atribSintetizados.get("TIPO");


                atribSintetizados2.put("CANTMEMORIA", cantMemoria);
                atribSintetizados2.put("TIPO", T);
                atribSintetizados2.put("PRIMERLIBREOFFSET", primerLibreOffset);
                atribSintetizados2.put("SIGUIENTEOFFSET", siguienteoffset);


                MepaH.generarInstruccion("RMEM "+ T.getCantElem());
            }
        }
        else{
            throw new ExcepcionErrorSintactico("Se esperaba ':', en linea: "+ ultimoToken.getLinea() + ", se obtuvo "+ ultimoToken.getLexema());
        }
        return atribSintetizados2;
    }



    private int restoDefinicionVariables(int offset) throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        int cantMemoria = 0, plo = 0;
        Hashtable HT;


        ultimoToken = alex.getToken();
        if (ultimoToken.isToken(Token.IDENTIFICADOR)){
            HT = declaracionVariable(offset);

            plo = (Integer) HT.get("PRIMERLIBREOFFSET");

            if (ultimoToken.isToken(Token.PUNTOCOMA)){
                //declaracion correcta
                cantMemoria = (Integer)HT.get("CANTMEMORIA") + restoDefinicionVariables(plo);
            }
        }

        return cantMemoria;

    }


    // ------------------------------- BLOQUE  --------------------------------------------




    private void bloque() throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        ultimoToken = alex.getToken();
        int etiqueta = TdS.nuevaEtiqueta();
        int cantMemoria = 0;
        
        parteDefinicionConstante();
        parteDefinicionTipos();
        cantMemoria = parteDefinicionVariable();

        MepaH.generarInstruccion("DSVS l"+etiqueta);
        
        parteDeclaracionProcFunc();
        MepaH.generarInstruccion("l"+etiqueta+" NADA");
        parteSentencias();

        MepaH.generarInstruccion("LMEM " + cantMemoria);
    }


    // ---------------------- PROCEDIMIENTOS Y FUNCIONES ----------------------------------


    private void parteDeclaracionProcFunc() throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        
        if (ultimoToken.isToken(Token.PROCEDURE)){
            declaracionProcedimiento();


            if (ultimoToken.isToken(Token.PUNTOCOMA)){
                // declaracion de procedimiento o funcion correcta
            } else { throw new ExcepcionErrorSintactico("Se esperaba ';' en la linea " + ultimoToken.getLinea()); }


            ultimoToken = alex.getToken();


            parteDeclaracionProcFunc();
        }
        else if (ultimoToken.isToken(Token.FUNCTION)) {
            declaracionFuncion();


            if (ultimoToken.isToken(Token.PUNTOCOMA)){
                // declaracion de procedimiento o funcion correcta
            } else { throw new ExcepcionErrorSintactico("Se esperaba ';' en la linea " + ultimoToken.getLinea()); }


            ultimoToken = alex.getToken();


            parteDeclaracionProcFunc();
        }

    }


    private void declaracionProcedimiento()throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        ArrayList<ParametroFormal> lista = new ArrayList<ParametroFormal>();
        String identificador;
        Procedimiento p;
        int etiqueta, cantMemoria = 0;
        etiqueta = TdS.nuevaEtiqueta();
        
        ultimoToken = alex.getToken();

        MepaH.generarInstruccion("l" + etiqueta  + " ENPR " + (TdS.getNivelLexico() + 1));

        if (ultimoToken.isToken(Token.IDENTIFICADOR)){
            identificador = ultimoToken.getLexema();

            if(!TdS.perteneceNivelActual(identificador)){

                if ((identificador.toUpperCase().equals("WRITE"))||(identificador.toUpperCase().equals("WRITELN"))|| (identificador.toUpperCase().equals("READ")) || (identificador.toUpperCase().equals("READLN")))
                {
                    throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador '"+identificador+"' corresponde a un metodo predefinido");
                }


                ultimoToken = alex.getToken();
                if (ultimoToken.isToken(Token.PUNTOCOMA)){
                    // Procedimiento sin parametros
                    lista = new ArrayList();

                    if(TdS.getTope().getIdentificador().equals(identificador.toUpperCase())){
                        throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador "+identificador+" es el identificador de funcion/procedimiento, no puede utilizarse en este nivel lexico");
                    }


                    TdS.apilarNivel(identificador);
                }
                else if (ultimoToken.isToken(Token.PARABRE)){
                    // Definicion de parametros

                    if(TdS.getTope().getIdentificador().equals(identificador.toUpperCase())){
                        throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador "+identificador+" es el identificador de funcion/procedimiento, no puede utilizarse en este nivel lexico");
                    }
                    TdS.apilarNivel(identificador);
                    declaracionParametrosFormales(lista);

                    if (ultimoToken.isToken(Token.PARCIERRA)){
                        ultimoToken = alex.getToken();
                        if (ultimoToken.isToken(Token.PUNTOCOMA)){

                        }
                        else {throw new ExcepcionErrorSintactico("Se esperaba ';', en linea: "+ ultimoToken.getLinea());}
                    }
                    else{
                        throw new ExcepcionErrorSintactico("Se esperaba ')' , en linea: "+ ultimoToken.getLinea());
                    }
                }
                else{
                    throw new ExcepcionErrorSintactico("Se esperaba ';' o '(', en linea: "+ ultimoToken.getLinea());
                }


                p = new Procedimiento(etiqueta, lista, TdS.getNivelLexico());

                ElementoDePila tope = TdS.getTope();
                TdS.desapilarNivel();
                //System.out.println(identificador + "-->" + p.getNivelLexico());
                TdS.getTope().registrarProcedimiento(identificador, p);
                TdS.apilarNivel(tope);

                bloque();

                TdS.desapilarNivel();


                cantMemoria = p.getCantMemoria();
                MepaH.generarInstruccion("RTPR "+ (p.getNivelLexico()) + " , " + cantMemoria);
                //TdS.desapilarNivel();
            } else { throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador '"+identificador+"' se encuentra en uso");}
        }else{
                throw new ExcepcionErrorSintactico("Se esperaba identificador, en linea: "+ ultimoToken.getLinea());
            }
    }


    private void declaracionFuncion() throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        ArrayList<ParametroFormal> lista = new ArrayList<ParametroFormal>();
        String identificador;
        Funcion f;
        Tipo t;
        int etiqueta, cantMemoria = 0;



        etiqueta = TdS.nuevaEtiqueta();

        ultimoToken = alex.getToken();

        MepaH.generarInstruccion("l" + etiqueta  + " ENPR " + (TdS.getNivelLexico() + 1));

        if (ultimoToken.isToken(Token.IDENTIFICADOR)){
            identificador = ultimoToken.getLexema();
            if(!TdS.perteneceNivelActual(identificador)){
            ultimoToken = alex.getToken();


            if (ultimoToken.isToken(Token.DOSPUNTOS)){
                ultimoToken = alex.getToken();
                if(TdS.getTope().getIdentificador().equals(identificador.toUpperCase())){
                        throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador "+identificador+" es el identificador de funcion/procedimiento, no puede utilizarse en este nivel lexico");
                    }
                TdS.apilarNivel(identificador);

                if (ultimoToken.isToken(Token.IDENTIFICADOR)){

                    t = tipoSimple();
                    if(t.esArreglo()){
                                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El resultado de la función debe ser de tipo simple");
                            }
                    //ultimoToken = alex.getToken();


                    if (ultimoToken.isToken(Token.PUNTOCOMA)){
                        //FUNCION SIN PARAMETROS
                        lista = new ArrayList();
                        
                    } else{ throw new ExcepcionErrorSintactico("Se esperaba ';', en linea: " + ultimoToken.getLinea());}
                } else{ throw new ExcepcionErrorSintactico("Se esperaba identificador de tipo, en linea: "+ ultimoToken.getLinea());}
            }
            else if (ultimoToken.isToken(Token.PARABRE)){
                // Definicion de parametros
                if(TdS.getTope().getIdentificador().equals(identificador.toUpperCase())){
                        throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador "+identificador+" es el identificador de funcion/procedimiento, no puede utilizarse en este nivel lexico");
                    }

                TdS.apilarNivel(identificador);
                declaracionParametrosFormales(lista);


                if (ultimoToken.isToken(Token.PARCIERRA)){
                    ultimoToken = alex.getToken();


                    if (ultimoToken.isToken(Token.DOSPUNTOS)){
                        ultimoToken = alex.getToken();


                        if (ultimoToken.isToken(Token.IDENTIFICADOR)){

                            t = tipoSimple();
                            if(t.esArreglo()){
                                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El resultado de la función debe ser de tipo simple");
                            }

                            //ultimoToken = alex.getToken();


                            if (ultimoToken.isToken(Token.PUNTOCOMA)){
                            //FUNCION CORRECTA
                            }else {throw new ExcepcionErrorSintactico("Se esperaba ';', en linea: "+ ultimoToken.getLinea());}
                        } else {throw new ExcepcionErrorSintactico("Se esperaba un identificador de tipo, en linea: "+ ultimoToken.getLinea());}
                    } else {throw new ExcepcionErrorSintactico("Se esperaba ':', en linea: "+ ultimoToken.getLinea());}
                } else {throw new ExcepcionErrorSintactico("Se esperaba ')' , en linea: "+ ultimoToken.getLinea());}
            } else {throw new ExcepcionErrorSintactico("Se esperaba ':' o '(', en linea: "+ ultimoToken.getLinea());        }
            } else { throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador '"+identificador+"' se encuentra en uso");}

        }
        else{
            throw new ExcepcionErrorSintactico("Se esperaba un identificador, en linea: "+ ultimoToken.getLinea());
        }



        f = new Funcion(etiqueta, lista, t, TdS.getNivelLexico());
        //TdS.getTope().registrarFuncion(identificador, f);
        // TdS.apilarNivel(identificador);
        ElementoDePila tope = TdS.getTope();
        TdS.desapilarNivel();
        
        TdS.getTope().registrarFuncion(identificador, f);
        TdS.apilarNivel(tope);

       bloque();

        TdS.desapilarNivel();
      
        cantMemoria = f.getCantMemoria();
        MepaH.generarInstruccion("RTPR " + (f.getNivelLexico() ) + " , " + cantMemoria);
       // TdS.desapilarNivel();
    }


    private void declaracionParametrosFormales(ArrayList lista2) throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        ArrayList<ParametroFormal> lista1 = new  ArrayList<ParametroFormal>();

        ultimoToken = alex.getToken();
        parametroFormal(lista1);
        restoDeclaracionParametrosFormales(lista2);

        lista2.addAll(0, lista1);
        
    }


    private void parametroFormal(ArrayList lista) throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        ParametroFormal p;
        int pasaje;
        Tipo t = new Tipo();

        
        if (ultimoToken.isToken(Token.VAR)){
            ultimoToken = alex.getToken();
            pasaje = REFERENCIA;
        }
        else{
            pasaje = VALOR;
        }

        if (ultimoToken.isToken(Token.IDENTIFICADOR)){
            String identificador = ultimoToken.getLexema();

            ultimoToken = alex.getToken();
            t = grupoDeParametrosFormales(lista, pasaje);

            p = new ParametroFormal(TdS.getNivelLexico(), 0, t, pasaje);
            TdS.getTope().registrarParametro(identificador, p);

            lista.add(0, p);
            
        }else {throw new ExcepcionErrorSintactico("Se esperaba un identificador, se encontro "+ultimoToken.getLexema()+" en la linea " + ultimoToken.getLinea()); }

    }

    private Tipo grupoDeParametrosFormales(ArrayList lista,int pasaje)throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        ParametroFormal p;
        Tipo tipoResultado = null;

        if (ultimoToken.isToken(Token.COMA)){
            ultimoToken = alex.getToken();

            if (ultimoToken.isToken(Token.IDENTIFICADOR)){
                String identificador = ultimoToken.getLexema();

                ultimoToken = alex.getToken();
                tipoResultado = grupoDeParametrosFormales(lista, pasaje);
                
                p = new ParametroFormal(TdS.getNivelLexico(), 0,tipoResultado, pasaje);
                TdS.getTope().registrarParametro(identificador, p);
                lista.add(0, p);

            } else { throw new ExcepcionErrorSintactico("Se esperaba un identificador, se encontro "+ultimoToken.getLexema()+" en la linea " + ultimoToken.getLinea()); }
        }
        else if (ultimoToken.isToken(Token.DOSPUNTOS)){
            
            ultimoToken = alex.getToken();

            if (ultimoToken.isToken(Token.IDENTIFICADOR)){
                tipoResultado = tipoSimple();
                lista = new ArrayList();

                
                
            }else { throw new ExcepcionErrorSintactico("Se esperaba un identificador de tipo en la linea " + ultimoToken.getLinea()); }
        }else { throw new ExcepcionErrorSintactico("Se esperaba ':' en la linea " + ultimoToken.getLinea()); }

        return tipoResultado;
    }



    private void restoDeclaracionParametrosFormales(ArrayList lista2) throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        ArrayList<ParametroFormal> lista1 = new ArrayList<ParametroFormal>();

        if (ultimoToken.isToken(Token.PUNTOCOMA)){
             ultimoToken = alex.getToken();

             if (ultimoToken.isToken(Token.IDENTIFICADOR) || ultimoToken.isToken(Token.VAR)){
                 parametroFormal(lista1);
                 restoDeclaracionParametrosFormales(lista2);
             }
             else{
                 throw new ExcepcionErrorSintactico("Se esperaba identificador o 'VAR' en la linea " + ultimoToken.getLinea());
             }
        }
        else if (ultimoToken.isToken(Token.PARCIERRA)){
            lista2 = new ArrayList();
        }
        else{
            throw new ExcepcionErrorSintactico("Se esperaba ';' en lugar de '" + ultimoToken.getLexema() + "' en la linea " + ultimoToken.getLinea());
        }


        lista2.addAll(0, lista1);

    }

    // ---------------------- SENTENCIAS ----------------------------------


    private void parteSentencias() throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{


        if (ultimoToken.isToken(Token.BEGIN)){
            ultimoToken = alex.getToken();


            if (ultimoToken.isToken(Token.IF)){
                /* La sentencia IF se trata como un caso aparte porque realiza el control del ';' final dentro
                 * de su propio metodo. En las otras sentencias el control del ';' final se realiza en este
                 * metodo */
                sentencia();
                restoSentencias();


                if (ultimoToken.isToken(Token.PUNTOCOMA)){
                    // Un bloque puede terminar con una sentencia seguida de ';' o con la sentencia sin ';'
                    ultimoToken = alex.getToken();
                }


                // Despues de todas las sentencias busca un end
                if (ultimoToken.isToken(Token.END)){
                // la parte de sentencias es correcta


                    ultimoToken = alex.getToken();
                } else { throw new ExcepcionErrorSintactico("Se esperaba ';' en la linea " + ultimoToken.getLinea()); }
            }
            else{
                sentencia();

                if (ultimoToken.isToken(Token.PUNTOCOMA)){

                    ultimoToken = alex.getToken();
                    restoSentencias();

                    if (ultimoToken.isToken(Token.PUNTOCOMA)){
                        // Un bloque puede terminar con una sentencia seguida de ';' o con la sentencia sin ';'
                        ultimoToken = alex.getToken();
                    }

                    // Despues de todas las sentencias busca un end
                    if (ultimoToken.isToken(Token.END)){
                    // la parte de sentencias es correcta

                        ultimoToken = alex.getToken();
                    } else { throw new ExcepcionErrorSintactico("Se esperaba ';' en la linea " + ultimoToken.getLinea()); }
                }
                else if (ultimoToken.isToken(Token.END)){
                    // Hay una sola sentencia y luego sigue el end
                    ultimoToken = alex.getToken();
                }
                else { throw new ExcepcionErrorSintactico("Se esperaba ';' en la linea " + ultimoToken.getLinea()); }
            }
        }
        else { throw new ExcepcionErrorSintactico("Se esperaba 'begin' en la linea " + ultimoToken.getLinea()); }
    }


    private void restoSentencias() throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        /* La sentencia if controla si finaliza con ';', debido a que es necesario para saber si despues de una
         * sentencia sigue un else, para esto se consume un token de mas, por lo que el manejo de una sentencia
         * if es diferente al otras sentencias */


        if (ultimoToken.isToken(Token.IF)){
            sentencia();
            restoSentencias();
        }
        else{
            sentencia();

            if (ultimoToken.isToken(Token.PUNTOCOMA)){
                ultimoToken = alex.getToken();
                restoSentencias();
            }
        }
    }


    private void sentenciaWrite(int tipoEscritura)throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        ultimoToken = alex.getToken();

        if (ultimoToken.isToken(Token.PARABRE)){
            ultimoToken = alex.getToken();
            Tipo t= expresion(VALOR);

            if (t.esBoolean() || t.esArreglo()){
                throw new ExcepcionErrorSemantico("En linea: " + ultimoToken.getLinea()+" La sentencia write solo admite parámetros de tipo entero");
            }else if (t.esSubrango()){
                MepaH.generarInstruccion("CONT "+((Subrango)t).getli() + " , "+((Subrango)t).getls());
            }else if (t.esEntero()){
                MepaH.generarInstruccion("CONT -32768, 32767");
            }



            if((ultimoToken.isToken(Token.PARCIERRA))&&(tipoEscritura==WRITELN)){
                MepaH.generarInstruccion("IMLN");
            }
            else{
                MepaH.generarInstruccion("IMPR");
            }
            
            restoSentenciaWrite(tipoEscritura);
        }
        else{
            throw new ExcepcionErrorSintactico("Se esperaba '(' en linea " + ultimoToken.getLinea());
        }
    }

    private void restoSentenciaWrite(int tipoEscritura)throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{

        if (ultimoToken.isToken(Token.COMA)){

            ultimoToken = alex.getToken();
            Tipo t = expresion(VALOR);
            if (t.esBoolean() || t.esArreglo()){
                throw new ExcepcionErrorSemantico("En linea: " + ultimoToken.getLinea()+" La sentencia write solo admite parámetros de tipo entero");
            }else if (t.esSubrango()){
                MepaH.generarInstruccion("CONT "+((Subrango)t).getli() + " , "+((Subrango)t).getls());
            }else if (t.esEntero()){
                MepaH.generarInstruccion("CONT -32768, 32767");
            }

            if((ultimoToken.isToken(Token.PARCIERRA))&&(tipoEscritura==WRITELN)){
                MepaH.generarInstruccion("IMLN");
            }
            else{
                MepaH.generarInstruccion("IMPR");
            }
            restoSentenciaWrite(tipoEscritura);

        }
        else if (ultimoToken.isToken(Token.PARCIERRA)){
            ultimoToken = alex.getToken();
        }
        else
        {
            throw new ExcepcionErrorSintactico("Se esperaba ')' o expresion en linea " + ultimoToken.getLinea());
        }
    }


    private void sentenciaRead(int tipoLectura)throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        String identificador;
        Elemento idObj;

        ultimoToken = alex.getToken();

        if (ultimoToken.isToken(Token.PARABRE)){
            ultimoToken = alex.getToken();

            identificador = ultimoToken.getLexema();

            if (TdS.pertenece(identificador)){
                idObj = TdS.getEntrada(identificador);

                if (idObj.esVariable()){
                    Variable idObjVAR = (Variable)idObj;

                    if (idObjVAR.getTipo().esArreglo()){
                        // Verifica si el tipo del parametro es entero
                        if (!TdS.compatibles(((Arreglo)(idObjVAR.getTipo())).getTipoElemento(), new Entero())){
                           throw new ExcepcionErrorSemantico("Se esperaba parametro de tipo entero en linea " + ultimoToken.getLinea());
                        }
  
                        restoSentenciaReadArreglo(((Arreglo)idObjVAR.getTipo()), tipoLectura);
                    }
                    else{
                        // Verifica si el tipo del parametro es entero
                        if (!TdS.compatibles(idObjVAR.getTipo(), new Entero())){
                            throw new ExcepcionErrorSemantico("Se esperaba parametro de tipo entero en linea " + ultimoToken.getLinea());
                        }
                    }

                    /*if (idObjVAR.getTipo().esSubrango()){
                        MepaH.generarInstruccion("CONT " + ((Subrango)(idObjVAR.getTipo())).getli() + " , " + ((Subrango)(idObjVAR.getTipo())).getls());
                    }*/

                    if(tipoLectura==READ){
                    MepaH.generarInstruccion("LEER");}
                    else {MepaH.generarInstruccion("LELN");}

                    if (idObjVAR.getTipo().esArreglo()){
                        Arreglo arr = ((Arreglo)idObjVAR.getTipo());

                        MepaH.generarInstruccion("CONT " + ((Subrango)arr.getTipoElemento()).getli() + " , "  + ((Subrango)arr.getTipoElemento()).getls());
                        MepaH.generarInstruccion("ALAR " + idObjVAR.getNivelLexico() + " , " + idObjVAR.getOffset());
                    }
                    else{

                        MepaH.generarInstruccion("CONT " + ((Subrango)idObjVAR.getTipo()).getli() + " , "  + ((Subrango)idObjVAR.getTipo()).getls());
                        MepaH.generarInstruccion("ALVL " + idObjVAR.getNivelLexico() + " , " + idObjVAR.getOffset());
                    }


                }
                else if(idObj.esParametro()){
                    ParametroFormal idObjPAR = (ParametroFormal)idObj;

                    if (idObjPAR.getTipo().esArreglo()){
                        // Verifica si el tipo del parametro es entero
                        if (!TdS.compatibles(((Arreglo)(idObjPAR.getTipo())).getTipoElemento(), new Entero())){
                           throw new ExcepcionErrorSemantico("Se esperaba parametro de tipo entero en linea " + ultimoToken.getLinea());
                        }

                        restoSentenciaReadArreglo(((Arreglo)idObjPAR.getTipo()),tipoLectura);
                    }
                    else{
                        // Verifica si el tipo del parametro es entero
                        if (!TdS.compatibles(idObjPAR.getTipo(), new Entero())){
                            throw new ExcepcionErrorSemantico("Se esperaba parametro de tipo entero en linea " + ultimoToken.getLinea());
                        }
                    }

                    if(tipoLectura==READ){
                    MepaH.generarInstruccion("LEER");}
                    else {MepaH.generarInstruccion("LELN");}

                    
                    /*if (idObjPAR.getTipo().esSubrango()){
                        MepaH.generarInstruccion("CONT " + ((Subrango)(idObjPAR.getTipo())).getli() + " , " + ((Subrango)(idObjPAR.getTipo())).getls());
                    }*/

                    if (idObjPAR.getTipo().esArreglo()){

                        Arreglo arr = (Arreglo)idObjPAR.getTipo();

                        if (idObjPAR.getPasaje() == VALOR){
                            MepaH.generarInstruccion("CONT " + ((Subrango)arr.getTipoElemento()).getli() + " , "  + ((Subrango)arr.getTipoElemento()).getls());
                            MepaH.generarInstruccion("ALAR " + idObjPAR.getNivelLexico() + " , " + idObjPAR.getOffset());
                        }
                        else{
                            MepaH.generarInstruccion("CONT " + ((Subrango)arr.getTipoElemento()).getli() + " , "  + ((Subrango)arr.getTipoElemento()).getls());
                            MepaH.generarInstruccion("ALAI " + idObjPAR.getNivelLexico() + " , " + idObjPAR.getOffset());
                        }
                    }
                    else{
                        if (idObjPAR.getPasaje() == VALOR){
                            MepaH.generarInstruccion("CONT " + ((Subrango)idObjPAR.getTipo()).getli() + " , "  + ((Subrango)idObjPAR.getTipo()).getls());
                            MepaH.generarInstruccion("ALVL " + idObjPAR.getNivelLexico() + " , " + idObjPAR.getOffset());
                        }
                        else{
                            MepaH.generarInstruccion("CONT " + ((Subrango)idObjPAR.getTipo()).getli() + " , "  + ((Subrango)idObjPAR.getTipo()).getls());
                            MepaH.generarInstruccion("ALVI " + idObjPAR.getNivelLexico() + " , " + idObjPAR.getOffset());
                        }
                    }
                }
                else{
                    throw new ExcepcionErrorSintactico("Se esperaba identificador de variable en linea " + ultimoToken.getLinea());
                }

                ultimoToken = alex.getToken();
            }
            else{
                throw new ExcepcionErrorSintactico("Se esperaba identificador en linea " + ultimoToken.getLinea());
            }

            restoSentenciaRead(tipoLectura);
        }
        else{
            throw new ExcepcionErrorSintactico("Se esperaba '(' en linea " + ultimoToken.getLinea());
        }
    }

    private void restoSentenciaRead(int tipoLectura)throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        Elemento idObj;
        String identificador;

        if (ultimoToken.isToken(Token.COMA)){

            ultimoToken = alex.getToken();

            identificador = ultimoToken.getLexema();

            if (TdS.pertenece(identificador)){
                idObj = TdS.getEntrada(identificador);

                if (idObj.esVariable()){
                    Variable idObjVAR = (Variable)idObj;
                    
                    if (idObjVAR.getTipo().esArreglo()){
                        // Verifica si el tipo del parametro es entero
                        if (!TdS.compatibles(((Arreglo)(idObjVAR.getTipo())).getTipoElemento(), new Entero())){
                           throw new ExcepcionErrorSemantico("Se esperaba parametro de tipo entero en linea " + ultimoToken.getLinea());
                        }

                        restoSentenciaReadArreglo(((Arreglo)idObjVAR.getTipo()),tipoLectura);
                    }
                    else{
                        // Verifica si el tipo del parametro es entero
                        if (!TdS.compatibles(idObjVAR.getTipo(), new Entero())){
                            throw new ExcepcionErrorSemantico("Se esperaba parametro de tipo entero en linea " + ultimoToken.getLinea());
                        }
                    }

                    /*if (idObjVAR.getTipo().esSubrango()){
                        MepaH.generarInstruccion("CONT " + ((Subrango)(idObjVAR.getTipo())).getli() + " , " + ((Subrango)(idObjVAR.getTipo())).getls());
                    }*/

                    if(tipoLectura==READ){
                        MepaH.generarInstruccion("LEER");}
                    else {
                        MepaH.generarInstruccion("LELN");}

                    if (idObjVAR.getTipo().esArreglo()){
                        Arreglo arr = ((Arreglo)idObjVAR.getTipo());

                        MepaH.generarInstruccion("CONT " + ((Subrango)arr.getTipoElemento()).getli() + " , "  + ((Subrango)arr.getTipoElemento()).getls());
                        MepaH.generarInstruccion("ALAR " + idObjVAR.getNivelLexico() + " , " + idObjVAR.getOffset());
                    }
                    else{
                      
                        MepaH.generarInstruccion("CONT " + ((Subrango)idObjVAR.getTipo()).getli() + " , "  + ((Subrango)idObjVAR.getTipo()).getls());
                        MepaH.generarInstruccion("ALVL " + idObjVAR.getNivelLexico() + " , " + idObjVAR.getOffset());
                    }
                }
                else if(idObj.esParametro()){
                    ParametroFormal idObjPAR = (ParametroFormal)idObj;

                    if (idObjPAR.getTipo().esArreglo()){
                        // Verifica si el tipo del parametro es entero
                        if (!TdS.compatibles(((Arreglo)(idObjPAR.getTipo())).getTipoElemento(), new Entero())){
                           throw new ExcepcionErrorSemantico("Se esperaba parametro de tipo entero en linea " + ultimoToken.getLinea());
                        }

                        restoSentenciaReadArreglo(((Arreglo)idObjPAR.getTipo()),tipoLectura);
                    }
                    else{
                        // Verifica si el tipo del parametro es entero
                        if (!TdS.compatibles(idObjPAR.getTipo(), new Entero())){
                            throw new ExcepcionErrorSemantico("Se esperaba parametro de tipo entero en linea " + ultimoToken.getLinea());
                        }
                    }

                    if(tipoLectura==READ){
                        MepaH.generarInstruccion("LEER");}
                    else {
                        MepaH.generarInstruccion("LELN");}

                    if (idObjPAR.getTipo().esArreglo()){
                        Arreglo arr = ((Arreglo)idObjPAR.getTipo());
                        
                        if (idObjPAR.getPasaje() == VALOR){
                            MepaH.generarInstruccion("CONT " + ((Subrango)arr.getTipoElemento()).getli() + " , "  + ((Subrango)arr.getTipoElemento()).getls());
                            MepaH.generarInstruccion("ALAR " + idObjPAR.getNivelLexico() + " , " + idObjPAR.getOffset());
                        }
                        else{
                            MepaH.generarInstruccion("CONT " + ((Subrango)arr.getTipoElemento()).getli() + " , "  + ((Subrango)arr.getTipoElemento()).getls());
                            MepaH.generarInstruccion("ALAI " + idObjPAR.getNivelLexico() + " , " + idObjPAR.getOffset());
                        }
                    }
                    else{
                        if (idObjPAR.getPasaje() == VALOR){
                            MepaH.generarInstruccion("CONT " + ((Subrango)idObjPAR.getTipo()).getli() + " , "  + ((Subrango)idObjPAR.getTipo()).getls());
                            MepaH.generarInstruccion("ALVL " + idObjPAR.getNivelLexico() + " , " + idObjPAR.getOffset());
                        }
                        else{
                            MepaH.generarInstruccion("CONT " + ((Subrango)idObjPAR.getTipo()).getli() + " , "  + ((Subrango)idObjPAR.getTipo()).getls());
                            MepaH.generarInstruccion("ALVI " + idObjPAR.getNivelLexico() + " , " + idObjPAR.getOffset());
                        }
                    }
                }
                else{
                    throw new ExcepcionErrorSemantico("Se esperaba identificador de variable en linea " + ultimoToken.getLinea());
                }

                ultimoToken = alex.getToken();

            }
            else{
                throw new ExcepcionErrorSintactico("Se esperaba identificador en linea " + ultimoToken.getLinea());
            }

            restoSentenciaRead(tipoLectura);

        }
        else if (ultimoToken.isToken(Token.PARCIERRA)){
            ultimoToken = alex.getToken();
        }
        else
        {
            throw new ExcepcionErrorSintactico("Se esperaba ')' o expresion en linea " + ultimoToken.getLinea());
        }
    }


    private void restoSentenciaReadArreglo(Arreglo idObj, int tipoLectura)throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        ultimoToken = alex.getToken();
        Tipo tipoExpresion;

        if (ultimoToken.isToken(Token.CORABRE)){
            ultimoToken = alex.getToken();
            tipoExpresion = expresionSimple(VALOR);

            if (!TdS.compatibles(tipoExpresion, idObj.getTipoIndice())){
                throw new ExcepcionErrorSemantico("Se intenta indexar un arreglo con un tipo incompatible con el tipo indice, en linea " + ultimoToken.getLinea());
            }

            MepaH.generarInstruccion("CONT " + idObj.getli() + " , " + idObj.getls());
            // Normalizo el indice
            MepaH.generarInstruccion("APCT " + idObj.getli());
            MepaH.generarInstruccion("SUST");

            if (ultimoToken.isToken(Token.CORCIERRA)){

            }
            else{
                throw new ExcepcionErrorSintactico("Se esperaba ']' en linea " + ultimoToken.getLinea());
            }

        }
        else{
            throw new ExcepcionErrorSemantico("El metodo read no permite parametros de tipo arreglo, error en linea " + ultimoToken.getLinea());
        }
    }



    private void sentencia() throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        String identificador="";
        Elemento idObj;

        if (ultimoToken.isToken(Token.IDENTIFICADOR)){
            identificador = ultimoToken.getLexema();

            if (identificador.toUpperCase().equals("WRITE")){
                sentenciaWrite(WRITE);
            }
            else if (identificador.toUpperCase().equals("WRITELN")){
                sentenciaWrite(WRITELN);
            }
            else if (identificador.toUpperCase().equals("READ")){
                sentenciaRead(READ);
            }
            else if (identificador.toUpperCase().equals("READLN")){
                sentenciaRead(READLN);
            }
            else
            {
                ultimoToken = alex.getToken();
                if (TdS.pertenece(identificador)){
                idObj = TdS.getEntrada(identificador);
                }else{
                   throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador " +identificador+" no está declarado");
                }
                if (ultimoToken.isToken(Token.PARABRE)){
                    if(idObj!=null){
                        if (idObj.esProcedimiento() /*|| idObj.esFuncion()*/){

                            // Debe ser una llamada a procedimiento
                            sentenciaProcedimientoFuncion(idObj);

                            if ((ultimoToken.isToken(Token.IDENTIFICADOR)) || (ultimoToken.isToken(Token.NUMERO))){
                                // si hay un identificador o un numero inmediatamente despues de un identificador
                                // suponemos que falta un operador de asignacion entre ambos
                                throw new ExcepcionErrorSintactico("Se esperaba ':=' en linea " + ultimoToken.getLinea());
                            }
                        }
                        else { throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se esperaba identificador de procedimiento" /* o funcion"*/); }
                    }
                    else { throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador "+identificador+" no corresponde a un idenficador de procedimiento o funcion"); }
                }
                else if (ultimoToken.isToken(Token.OPASIGN)){
                    // Verifico si es una asignacion

                    sentenciaAsignacion(identificador);
                }
                else if (ultimoToken.isToken(Token.CORABRE)){

                    if (idObj.esVariable()){
                        if (((Variable)idObj).getTipo().esArreglo()){
                            Arreglo a = ((Arreglo)(((Variable)idObj).getTipo()));
                            sentenciaArreglo(a);

                            //normalizo el indice del arreglo destino
                            MepaH.generarInstruccion("CONT " + ((Arreglo)((Variable)idObj).getTipo()).getli() + " , " + ((Arreglo)((Variable)idObj).getTipo()).getls());
                            MepaH.generarInstruccion("APCT "+ ((Arreglo)((Variable)idObj).getTipo()).getli());
                            MepaH.generarInstruccion("SUST");

                            if (ultimoToken.isToken(Token.OPASIGN)){
                                // Verifico si es una asignacion
                                sentenciaAsignacionElemArreglo(identificador);
                            }
                            else if ((ultimoToken.isToken(Token.IDENTIFICADOR)) || (ultimoToken.isToken(Token.NUMERO))){
                                // si hay un identificador o un numero inmediatamente despues de un identificador de arreglo
                                // suponemos que falta un operador de asignacion entre ambos
                                throw new ExcepcionErrorSintactico("Se esperaba ':=' en linea " + ultimoToken.getLinea());
                            }
                        }
                        else{
                            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se esperaba identificador de tipo arreglo");
                        }
                    }
                    else if (idObj.esParametro()){
                        if (((ParametroFormal)idObj).getTipo().esArreglo()){
                            Arreglo a = ((Arreglo)(((ParametroFormal)idObj).getTipo()));

                            sentenciaArreglo(a);

                            //normalizo el indice del arreglo destino
                            MepaH.generarInstruccion("CONT " + ((Arreglo)((ParametroFormal)idObj).getTipo()).getli() + " , " + ((Arreglo)((ParametroFormal)idObj).getTipo()).getls());
                            MepaH.generarInstruccion("APCT "+ ((Arreglo)((ParametroFormal)idObj).getTipo()).getli());
                            MepaH.generarInstruccion("SUST");

                            if (ultimoToken.isToken(Token.OPASIGN)){
                                // Verifico si es una asignacion
                                sentenciaAsignacionElemArreglo(identificador);
                            }
                            else if ((ultimoToken.isToken(Token.IDENTIFICADOR)) || (ultimoToken.isToken(Token.NUMERO))){
                                // si hay un identificador o un numero inmediatamente despues de un identificador de arreglo
                                // suponemos que falta un operador de asignacion entre ambos
                                throw new ExcepcionErrorSintactico("Se esperaba ':=' en linea " + ultimoToken.getLinea());
                            }
                        }
                        else{
                            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se esperaba identificador de tipo arreglo");
                        }
                    }
                    
                }else if(ultimoToken.isToken(Token.PUNTOCOMA)){
                    if (idObj.esProcedimiento()){
                        
                         if (((Procedimiento)idObj).getCantidadParametros()==0){
                            MepaH.generarInstruccion ("LLPR " + "l" + ((Procedimiento)idObj).getEtiqueta());
                        } else{
                            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Parametros insuficientes");
                        }
                    }else {
                        throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se esperaba identificador de procedimiento");
                    }
                }
                else if ((ultimoToken.isToken(Token.IDENTIFICADOR)) || (ultimoToken.isToken(Token.NUMERO))){
                    // si hay un identificador o un numero inmediatamente despues de un identificador
                    // suponemos que falta un operador de asignacion entre ambos
                    throw new ExcepcionErrorSintactico("Se esperaba ':=' en linea " + ultimoToken.getLinea());
                }

            }
        }
        else if  (ultimoToken.isToken(Token.WHILE)){
            BucleRepetir();//es el bucle de repetición, controlo sintaxis en el método
        }
        else if (ultimoToken.isToken(Token.IF)) {
            SentenciaCondicional();//es una sentencia condicional
        }
        else if (ultimoToken.isToken(Token.ELSE)){
            throw new ExcepcionErrorSintactico("Se esperaba sentencia en linea: " + ultimoToken.getLinea() + ". Se encontro 'else'");
        }
    }

    

    private void sentenciaProcedimientoFuncion(Elemento idObj) throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        // Ya se leyo el token PARABRE
        if(idObj.esProcedimiento()){
            if (((Procedimiento)idObj).getCantidadParametros()==0){
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El procedimiento no puede ser invocada con parámetros");
            }
        }
        declaracionParametrosActuales(idObj);

        if (ultimoToken.isToken(Token.PARCIERRA)){
            ultimoToken = alex.getToken();

            if (idObj.esProcedimiento()){
                // genera la llamada a procedimiento
                MepaH.generarInstruccion ("LLPR " + "l" + ((Procedimiento)idObj).getEtiqueta());
            }
            else{
                // genera la llamada a procedimiento
                //MepaH.generarInstruccion ("LLPR " + "l" + ((Funcion)idObj).getEtiqueta());
            }

        } else { throw new ExcepcionErrorSintactico("Se esperaba ')' en la linea " + ultimoToken.getLinea()); }
    }


    private void sentenciaArreglo(Arreglo a) throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        // Ya se leyo el token CORABRE
        ultimoToken = alex.getToken();
        int pasaje = VALOR;

        Tipo tipoExpresion = expresion(pasaje);

        // Controla que el tipo de la expresion sea de tipo simple
        tipoSimple(tipoExpresion);

        if (TdS.compatibles(a.getTipoIndice(), tipoExpresion)){
            if (ultimoToken.isToken(Token.CORCIERRA)){
                ultimoToken = alex.getToken();
            } else { throw new ExcepcionErrorSintactico("Se esperaba ']' en la linea " + ultimoToken.getLinea()); }
        }
        else{
            throw new ExcepcionErrorSemantico(("En linea: " + ultimoToken.getLinea() + ". El tipo de la expresion no es compatible con el tipo indice del arreglo"));
        }
    }


    private void sentenciaAsignacionElemArreglo(String identificador)throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        
        // Ya se leyo el lexema ':='
        Elemento idObj;
        Tipo idObjTIPO =null;
        int pasaje;

        if (TdS.pertenece(identificador)){
            idObj = TdS.getEntrada(identificador);

            if (idObj.esParametro()){
                pasaje = VALOR; //((ParametroFormal)idObj).getPasaje();
                idObjTIPO = ((ParametroFormal)idObj).getTipo();
            }
            else if ((idObj.esVariable()) || (idObj.esFuncion())){
                pasaje = VALOR;

                if (idObj.esVariable()){
                    idObjTIPO = ((Variable)idObj).getTipo();

                    if (((Variable)idObj).getTipo().esArreglo()){
                      //normalizo el indice del arreglo destino
                        /*MepaH.generarInstruccion("APCT "+ ((Arreglo)((Variable)idObj).getTipo()).getli());
                        MepaH.generarInstruccion("SUST");*/
                    }

                }
                else{
                    idObjTIPO = ((Funcion)idObj).getTipo();
                }
            }
            else{
                // no es variable, parametro o identificador de funcion por lo que no se le puede asignar un valor
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se esperaba identificador de variable o funcion como lado izquierdo de la asignacion");
            }

            ultimoToken = alex.getToken();

            Tipo tipoExpresion = expresionSimple(pasaje);
            //---------------
            Tipo TipoElementoArreglo=new Tipo();
            if (idObj.esVariable()){
                Variable IDOV = (Variable)idObj;
                TipoElementoArreglo= ((Arreglo)IDOV.getTipo()).getTipoElemento();
            }else{
                if(idObj.esParametro()){
                    ParametroFormal IDOP = (ParametroFormal)idObj;
                    TipoElementoArreglo= ((Arreglo)IDOP.getTipo()).getTipoElemento();
                }
            }
            if (TipoElementoArreglo.esBoolean()){
                MepaH.generarInstruccion("CONT 0,1");
            }else{
                if (TipoElementoArreglo.esSubrango()){
                    MepaH.generarInstruccion("CONT "+((Subrango)TipoElementoArreglo).getli()+" , "+((Subrango)TipoElementoArreglo).getls());
                }
            }
            //---------------

            if (TdS.compatibles(((Arreglo)idObjTIPO).getTipoElemento(), tipoExpresion)){
                if (idObj.esParametro()){
                    ParametroFormal idObjPar = (ParametroFormal) idObj;
                    // Dependiendo del tipo de pasaje genera una instruccion MEPa diferente
                    
                    if (idObjPar.getPasaje() == VALOR){
                        // PASAJE POR VALOR
                        if (idObjPar.getTipo().esArreglo()){
                            // Es una asignacion de un elemento de un arreglo pasado por valor
                            MepaH.generarInstruccion("ALAR " + idObjPar.getNivelLexico()+" , " + idObjPar.getOffset());
                        }
                        else{
                            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se esperaba identificador de variable o parametro de tipo arreglo");
                        }
                    }
                    else{
                        // PASAJE POR REFERENCIA
                        if (idObjPar.getTipo().esArreglo()){
                            // Es una asignacion de un arreglo completo pasado por referencia
                            MepaH.generarInstruccion("ALAI "+  idObjPar.getNivelLexico()+" , "+ idObjPar.getOffset());
                        }
                        else{
                            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se esperaba identificador de variable o parametro de tipo arreglo");
                        }
                    }
                }
                else if (idObj.esVariable()){
                    Variable idObjVAR = (Variable)idObj;

                    if (idObjVAR.getTipo().esArreglo()){
                        // Es una asignacion de un elemento de un arreglo pasado por valor
                        MepaH.generarInstruccion("ALAR " + idObjVAR.getNivelLexico()+" , " + idObjVAR.getOffset());
                    }
                    else{
                        throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se esperaba identificador de variable o parametro de tipo arreglo");
                    }
                }
                else{
                    throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se esperaba identificador de variable o parametro");
                }
            }
            else{
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Los tipos involucrados no son compatibles");
            }
        }
        else{
            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador "+identificador+" no está definido");
        }
    }


    private void sentenciaAsignacion(String identificador) throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        // el lado derecho de una asignacion puede ser:
        // * un identificador o una constante numerica
        // * una llamada a funcion (con o sin parametros)
        // * un valor de un arreglo
        // * una expresion

        // Ya se leyo el lexema ':='
        Elemento idObj;
        Tipo idObjTIPO =null;
        int pasaje;

        if (TdS.pertenece(identificador)){
            idObj = TdS.getEntrada(identificador);

            if (idObj.esParametro()){
                pasaje = VALOR; //((ParametroFormal)idObj).getPasaje();
                idObjTIPO = ((ParametroFormal)idObj).getTipo();
            }
            else if ((idObj.esVariable()) || (idObj.esFuncion())){
                pasaje = VALOR;

                if (idObj.esVariable()){
                    idObjTIPO = ((Variable)idObj).getTipo();

                    if (((Variable)idObj).getTipo().esArreglo()){
                      //normalizo el indice del arreglo destino
                        MepaH.generarInstruccion("APCT "+ ((Arreglo)((Variable)idObj).getTipo()).getli());
                        MepaH.generarInstruccion("SUST");
                    }

                }
                else{
                    idObjTIPO = ((Funcion)idObj).getTipo();
                }
            }
            else{
                // no es variable, parametro o identificador de funcion por lo que no se le puede asignar un valor
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se esperaba identificador de variable o funcion como lado izquierdo de la asignacion");
            }

            ultimoToken = alex.getToken();
            Tipo tipoExpresion = expresion(pasaje);

            if (TdS.compatibles(idObjTIPO, tipoExpresion)){
                if (idObj.esParametro()){
                    ParametroFormal idObjPar = (ParametroFormal) idObj;
                    // Dependiendo del tipo de pasaje genera una instruccion MEPa diferente
                    if (idObjPar.getPasaje() == VALOR){
                        // PASAJE POR VALOR
                        if (idObjPar.getTipo().esArreglo()){
                            // Es una asignacion de un arreglo completo pasado por valor
                            MepaH.generarInstruccion("POAR " + idObjPar.getNivelLexico()+" , " + idObjPar.getOffset() + " , " + ((Arreglo)idObjPar.getTipo()).getCantElem());
                        }
                        else if (idObjPar.getTipo().esSubrango()){
                            // Controlo los indices del subrango
                            MepaH.generarInstruccion("CONT " +((Subrango)idObjPar.getTipo()).getli()+" , "+ ((Subrango)idObjPar.getTipo()).getls());
                            MepaH.generarInstruccion("ALVL " + idObjPar.getNivelLexico()+" , "+ idObjPar.getOffset());
                        }
                        else{
                            // Es una asignacion de un parametro de tipo simple pasado por valor
                            MepaH.generarInstruccion("ALVL " +idObjPar.getNivelLexico()+" , "+ idObjPar.getOffset());
                        }
                    }
                    else{
                        // PASAJE POR REFERENCIA
                        if (idObjPar.getTipo().esArreglo()){
                            // Es una asignacion de un arreglo completo pasado por referencia
                            MepaH.generarInstruccion("POAI "+  idObjPar.getNivelLexico()+" , "+ idObjPar.getOffset()+" , "+ ((Arreglo)idObjPar.getTipo()).getCantElem());
                        }
                        else if (idObjPar.getTipo().esSubrango()){
                            // utilizo la operacion de chequeo de rangos provista por MEPa
                            MepaH.generarInstruccion("CONT " +((Subrango)idObjPar.getTipo()).getli()+" , "+ ((Subrango)idObjPar.getTipo()).getls());
                            MepaH.generarInstruccion("ALVI " + idObjPar.getNivelLexico()+" , "+ idObjPar.getOffset());
                        }
                        else{
                            // Es una asignacion de un parametro de tipo simple pasado por referencia
                            MepaH.generarInstruccion("ALVI " + idObjPar.getNivelLexico()+" , "+ idObjPar.getOffset());
                        }
                    }
                }
                else if (idObj.esVariable()){
                    Variable idObjVAR = (Variable)idObj;

                    if (idObjVAR.getTipo().esArreglo()){
                        // Es una asignacion de un arreglo completo pasado por valor
                        MepaH.generarInstruccion("POAR "+ idObjVAR.getNivelLexico()+" , "+ idObjVAR.getOffset() + " , " + ((Arreglo)idObjVAR.getTipo()).getCantElem());
                    }
                    else if (idObjVAR.getTipo().esSubrango()){
                        // utilizo la operacion de chequeo de rangos provista por MEPa
                        MepaH.generarInstruccion("CONT " +((Subrango)idObjVAR.getTipo()).getli()+" , "+ ((Subrango)idObjVAR.getTipo()).getls());
                        MepaH.generarInstruccion("ALVL " + idObjVAR.getNivelLexico()+" , "+ idObjVAR.getOffset());
                    }
                    else{
                        // es una variable de tipo simple
                        MepaH.generarInstruccion("ALVL " + idObjVAR.getNivelLexico()+" , "+ idObjVAR.getOffset());
                    }
                }
                else if (idObj.esFuncion()){
                    // getNombreNivel retorna el nivel del procedimiento o funcion asociado al nivel lexico actual
                    Funcion idObjFCN = (Funcion) idObj;
   
                    if (TdS.getTope().getIdentificador().toUpperCase().equals(identificador.toUpperCase())){
                        MepaH.generarInstruccion("ALVL " + (idObjFCN.getNivelLexico()) + " , "+ idObjFCN.getOffset());
                    }
                    else{
                        throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". No se puede asignar un valor a un identificador de funcion fuera del nivel lexico local a la misma");
                    }
                }
                else{
                    throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se esperaba identificador de variable o funcion");
                }
            }
            else{
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Los tipos involucrados no son compatibles");
            }
        }
        else{
            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador "+identificador+" no está definido");
        }
    }


    private void declaracionParametrosActuales(Elemento idObj) throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        ultimoToken = alex.getToken();
        int posicionParametro = 1;

        parametroActual(idObj, posicionParametro);
        int cantidadparametrosactuales = restoParametrosActuales(idObj, posicionParametro + 1);
        if(cantidadparametrosactuales-1 != ((Procedimiento)idObj).getCantidadParametros()){
            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". La cantidad de parametros esperados es " +((Procedimiento)idObj).getCantidadParametros()+" se obtuvo "+ (cantidadparametrosactuales-1));
        }
    }


    private void parametroActual(Elemento idObj, int posicionParametro) throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        //reemplazar valor por algun pasaje si es necesario.
        Tipo t;
        ParametroFormal p;

        if (idObj.esFuncion()){
            p = (ParametroFormal)(((Funcion)idObj).getParametro(posicionParametro));

            if (p == null){
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Demasiados parametros actuales");
            }

            t = expresion(p.getPasaje());

            if (!(TdS.compatibles(p.getTipo(), t))){
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El tipo del parametro actual no es el esperado");
            }
        }
        else{ //idObj es Procedimiento
            p = (ParametroFormal)(((Procedimiento)idObj).getParametro(posicionParametro));

            if (p == null){
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Demasiados parametros actuales");
            }

            t = expresion(p.getPasaje());

            if (!(TdS.compatibles(p.getTipo(), t))){
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El tipo del parametro actual no es el esperado");
            }
        }
    }


    private int restoParametrosActuales(Elemento idObj, int posicionParametro) throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico{
        int cantidadDeParametros = 1;
        if (ultimoToken.isToken(Token.COMA)){
            // Parametro correcto
            ultimoToken = alex.getToken();
            parametroActual(idObj, posicionParametro);
            cantidadDeParametros=restoParametrosActuales(idObj, posicionParametro + 1);
        }
        else if (ultimoToken.isToken(Token.PARCIERRA)){
            cantidadDeParametros=posicionParametro;
            if (idObj.esFuncion()){
                if (((Funcion)idObj).getCantidadParametros() > posicionParametro){
                    throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Insuficientes parametros actuales");
                }
            }
            else if (idObj.esProcedimiento()){
                if (((Procedimiento)idObj).getCantidadParametros() > posicionParametro){
                    throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Insuficientes parametros actuales");
                }
            }
        }
        return cantidadDeParametros;
    }




    // ------------------------------- EXPRESIONES  --------------------------------------------


    private Tipo expresion(int pasaje)throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico {
        
        Tipo tipoResultado;

        /*if ((ultimoToken.isToken(Token.OPSUMA)) && (!ultimoToken.getLexema().toLowerCase().equals("or"))){
            //ultimoToken = alex.getToken();
            tipoResultado = factor(pasaje);
        }
        else{
            tipoResultado = expresionSimple(pasaje);
        }*/

        tipoResultado = expresionSimple(pasaje);

        if (esRelacional(ultimoToken)){
            String operadorRelacional = ultimoToken.getLexema();
            ultimoToken = alex.getToken();
            Tipo tipoExp2 = expresionSimple(pasaje);
            
            if (operadorRelacional.equals("=")){
                MepaH.generarInstruccion("CMIG");
            }
            else if(operadorRelacional.equals("<>")){
                MepaH.generarInstruccion("CMDG");
            }
            else if(operadorRelacional.equals("<=")){
                MepaH.generarInstruccion("CMNI");
            }
            else if(operadorRelacional.equals(">=")){
                MepaH.generarInstruccion("CMYI");
            }
            else if(operadorRelacional.equals("<")){
                MepaH.generarInstruccion("CMME");
            }
            else if(operadorRelacional.equals(">")){
                MepaH.generarInstruccion("CMMA");
            }
            
            if(TdS.compatibles(tipoResultado, tipoExp2)){
                if ((!tipoResultado.esArreglo())){
                    if((!tipoResultado.esEntero()) &&(!tipoResultado.esSubrango()) ){
                        if((!operadorRelacional.equals("<>"))&&(!operadorRelacional.equals("="))){
                           throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Solo los operadores relacionales '=' y '<>' permiten tipos no enteros");
                        }
                    }
                }else{throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". No se permiten operaciones relacionales con arreglos");}
            }else{
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". No se permiten expresiones mixtas");
            }

            tipoResultado = new Booleano();
        }
        return tipoResultado;
    }


    // Metodo auxiliar que retorna true si el token pasado como parametro es un operador relacional
    private boolean esRelacional(Token t){
        return (t.isToken(Token.OPDIST) || t.isToken(Token.OPIGUAL) || t.isToken(Token.OPMAYOR) || t.isToken(Token.OPMAYORIG) || t.isToken(Token.OPMENOR) || t.isToken(Token.OPMENORIG));
    }


    /* Metodo para evaluar una expresion
     * E -> TE'
     * */
    private Tipo expresionSimple(int pasaje) throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico {
        Tipo tipoTermino, tipoResultado;
        tipoTermino = termino(pasaje);

        tipoResultado = restoExpresion(pasaje, tipoTermino);

        //if (tipoResultado == null){
            return tipoTermino;
        /*}
        else{
            return tipoResultado;
        }*/
    }


    /* E' -> +TE'
     * E' -> vacio
     * */
    private Tipo restoExpresion(int pasaje, Tipo tipoExp)throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico {
        Tipo tipoResultado = null;
        if ((ultimoToken.isToken(Token.OPSUMA))){
            String operador = ultimoToken.getLexema();
            Token tokenOp = ultimoToken;
            ultimoToken = alex.getToken();            

            if (operador.toUpperCase().equals("OR")){
               tipoResultado = restoExpresionOR(pasaje, tipoExp);
            }
            else if(operador.toUpperCase().equals("+")){
                tipoResultado = restoExpresionSUMA(pasaje, tipoExp);
            }
            else if(operador.toUpperCase().equals("-")){
                tipoResultado = restoExpresionRESTA(pasaje, tipoExp);
            }
            else if(esRelacional(tokenOp)){
               throw new ExcepcionErrorSintactico("El operador "+tokenOp.getLexema()+ " no pertenece a una expresion simple: " + ultimoToken.getLinea());
            }

        }else{
            tipoResultado = null;
        }
        return tipoResultado;
    }


    private Tipo restoExpresionOR(int pasaje, Tipo tipoExpresion)throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico {
        Tipo tipoTermino;
        Tipo tipoResultado=null;
        if (pasaje == REFERENCIA){
            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se requiere un parametro pasado por referencia (no se permite un valor constante)");
        }

        tipoTermino = termino(pasaje);

        if((tipoExpresion.esArreglo()) || (tipoTermino.esArreglo())){
            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". No se puede operar con arreglos completos");
        }


        if (TdS.compatibles(tipoTermino, tipoExpresion)){
            if (tipoTermino.esBoolean()){
                MepaH.generarInstruccion("DISJ");
                restoExpresion(pasaje, new Booleano());
                tipoResultado = new Booleano();
            }
            else{
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". No se permiten operandos enteros para el operador 'OR'");
            }
        }
        else{
            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". No se permiten expresiones mixtas");
        }

        return tipoResultado;
    }


    private Tipo restoExpresionSUMA(int pasaje, Tipo tipoExpresion)throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico {
        Tipo tipoTermino;
        Tipo tipoResultado=null;

        if (pasaje == REFERENCIA){
            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se requiere un parametro pasado por referencia (no se permite un valor constante)");
        }

        tipoTermino = termino(pasaje);

        if((tipoExpresion.esArreglo()) || (tipoTermino.esArreglo())){
            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". No se puede operar con arreglos completos");
        }

        if (TdS.compatibles(tipoTermino, tipoExpresion)){

            if (tipoTermino.esEntero() || tipoTermino.esSubrango()){
                MepaH.generarInstruccion("SUMA");
                restoExpresion(pasaje, new Entero());
                tipoResultado = new Entero();
            }
            else{
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Solo se permiten operandos enteros para el operador '+'");
            }
        }
        else{
            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". No se permiten expresiones mixtas");
        }

        return tipoResultado;
    }




    private Tipo restoExpresionRESTA(int pasaje, Tipo tipoExpresion)throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico {
        Tipo tipoTermino;
        Tipo tipoResultado=null;
        if (pasaje == REFERENCIA){
            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se requiere un parametro pasado por referencia (no se permite un valor constante)");
        }

        tipoTermino = termino(pasaje);

        if((tipoExpresion.esArreglo()) || (tipoTermino.esArreglo())){
            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". No se puede operar con arreglos completos");
        }

        if (TdS.compatibles(tipoTermino, tipoExpresion)){
            if (tipoTermino.esEntero() || tipoTermino.esSubrango()){
                    MepaH.generarInstruccion("SUST");
                    restoExpresion(pasaje, new Entero());
                    tipoResultado = new Entero();
            }
            else{
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Solo se permiten operandos enteros para el operador '-'");
            }
        }
        else{
            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". No se permiten expresiones mixtas");
        }

        return tipoResultado;
    }


    /* T -> FT'
     * */
    private Tipo termino(int pasaje) throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico {
        Tipo tipoFactor, tipoResultado;
        tipoFactor = factor(pasaje);
        
        tipoResultado=restoTermino(pasaje, tipoFactor);

        if (tipoResultado == null){
            return tipoFactor;
        }
        else{
            return tipoResultado;
        }
    }


    /* T' -> *FT'
     * T' -> vacio
     * */
    private Tipo restoTermino(int pasaje, Tipo tipoTermino)throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico {
        Tipo tipoResultado = null;
        if ((ultimoToken.isToken(Token.OPMULT))){
            String operador = ultimoToken.getLexema();
            ultimoToken = alex.getToken();

            if (operador.toUpperCase().equals("AND")){
                tipoResultado = restoTerminoAND(pasaje, tipoTermino);
            }
            else if(operador.toUpperCase().equals("*")){
                tipoResultado = restoTerminoPOR(pasaje, tipoTermino);
            }
            else if(operador.toUpperCase().equals("DIV")){
                tipoResultado = restoTerminoDIV(pasaje, tipoTermino);
            }
            return tipoResultado;

        }
        else{
            return null;
        }

    }

    private Tipo restoTerminoAND(int pasaje, Tipo tipoTermino)throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico {
        Tipo tipoResultado = null;
        if (pasaje == REFERENCIA){
            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se requiere un parametro pasado por referencia (no se permite un valor constante)");
        }
        Tipo tipoFactor = factor(pasaje);

        if ((tipoTermino.esArreglo()) || (tipoFactor.esArreglo())){
        throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". No se puede operar con arreglos completos");
        }

        if (TdS.compatibles(tipoFactor,tipoTermino)){
            if (tipoFactor.esBoolean()){
                MepaH.generarInstruccion("CONJ");
                restoTermino(pasaje, new Booleano());
                tipoResultado =  new Booleano();
            }
            else{
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Solo se permiten operandos de tipo booleano para el operador 'AND'.");
            }
        }
        else{
            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". No se permiten operaciones mixtas.");
        }
        return tipoResultado;
    }


    private Tipo restoTerminoPOR(int pasaje, Tipo tipoTermino)throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico {
        Tipo tipoResultado = null;
        if (pasaje == REFERENCIA) {
            //cual es el valor aca!!!
            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se requiere un parametro pasado por referencia (no se permite un valor constante)");
        }


        Tipo tipoFactor = factor(pasaje);
        if ((tipoTermino.esArreglo()) || (tipoFactor.esArreglo())){
            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". No se puede operar con arreglos completos");
        }


        if (TdS.compatibles(tipoFactor, tipoTermino)){
            if (tipoFactor.esEntero() || tipoFactor.esSubrango()){
                MepaH.generarInstruccion("MULT");
                restoTermino(pasaje, new Entero());
                tipoResultado =  new Entero();
            }
            else{
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Solo se permiten operandos de tipo booleano para el operador '*'.");
            }
        }
        else{
            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". No se permiten operaciones mixtas.");
        }

        return tipoResultado;
    }


    private Tipo restoTerminoDIV(int pasaje, Tipo tipoTermino)throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico {
        Tipo tipoResultado = null;
        if (pasaje == REFERENCIA) {
            //cual es el valor aca!!!
            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se requiere un parametro pasado por referencia (no se permite un valor constante)");
        }


        Tipo tipoFactor = factor(pasaje);
        if ((tipoTermino.esArreglo()) || (tipoFactor.esArreglo())){
            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". No se puede operar con arreglos completos");
        }


        if (TdS.compatibles(tipoFactor, tipoTermino)){
            if (tipoFactor.esEntero() || tipoFactor.esSubrango()){
                //controla que no sea división por cero.
                MepaH.generarInstruccion("DIVC");
                MepaH.generarInstruccion("DIVI");
                restoTermino(pasaje, new Entero());
                tipoResultado =  new Entero();
            }
            else{
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Solo se permiten operandos de tipo enteros para el operador 'DIV'.");
            }
        }
        else{
            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". No se permiten operaciones mixtas.");
        }

        return tipoResultado;
    }


    /* F -> ( expresion )
     * F -> not F
     * F -> variable
     * F -> designador de funcion
     * F -> constante sin signo
     * */
    private Tipo factor(int Pasaje) throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico {
        Tipo T;
        Tipo tipoResultado=null;

        /* Se pueden tener factores como -1, +1, -a, +a, -f(a), +f(a) */
        if (ultimoToken.isToken(Token.OPSUMA) && !(ultimoToken.getLexema().toUpperCase().equals("OR"))){
            String operador;
            operador = ultimoToken.getLexema();

            ultimoToken = alex.getToken();

            if (ultimoToken.isToken(Token.OPSUMA)){
                throw new ExcepcionErrorSintactico("Factor incorrecto en linea: " + ultimoToken.getLinea());
            }
            else{
                if (!(ultimoToken.getLexema().toUpperCase().equals("OR"))){
                    tipoResultado = factor(Pasaje);
                }
                else {
                    throw new ExcepcionErrorSintactico("Factor incorrecto en linea: " + ultimoToken.getLinea());
                }
            }

            if (TdS.compatibles(tipoResultado, new Entero())){
                // Si el token leido era un - multiplica el valor del factor por -1
                if (operador.equals("-")){
                    MepaH.generarInstruccion("APCT " + "-1");
                    MepaH.generarInstruccion("MULT");
                }
            }
            else{
                throw new ExcepcionErrorSemantico("Tipos incompatibles en linea: "+ ultimoToken.getLinea());
            }
        }
        else{
            if (ultimoToken.isToken(Token.PARABRE)){

                if (Pasaje == REFERENCIA){
                    throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se requiere un parametro pasado por referencia (no se permite un valor constante)");
                }

                ultimoToken = alex.getToken();
                tipoResultado = expresion(Pasaje);



                if (ultimoToken.isToken(Token.PARCIERRA)){
                    // expresion correcta
                    ultimoToken = alex.getToken();
                } else { throw new ExcepcionErrorSintactico("Se esperaba ')' en la linea " + ultimoToken.getLinea()); }
            }
            else if (ultimoToken.isToken(Token.OPNOT)){
                ultimoToken = alex.getToken();
                if (Pasaje == REFERENCIA){
                    throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se requiere un parametro pasado por referencia (no se permite un valor constante)");
                }
                T = factor(Pasaje);


                if (T.esArreglo()){
                    throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". No se puede operar con arreglos completos");
                }

                MepaH.generarInstruccion("NEGA");
                return T;
            }
            else if (ultimoToken.isToken(Token.IDENTIFICADOR)){

                Elemento idObj;
                String lexema;
                if (TdS.pertenece(ultimoToken.getLexema())){

                    idObj = TdS.getEntrada(ultimoToken.getLexema());
                    lexema = ultimoToken.getLexema();

                    ultimoToken = alex.getToken();
                    tipoResultado = restoFactor(idObj, lexema, Pasaje);
                    
                }
                else{
                    throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El identificador "+ultimoToken.getLexema()+" no está definido");
                }
            }
            else if (ultimoToken.isToken(Token.NUMERO)){
                if (Pasaje == REFERENCIA){
                    throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se requiere un parametro pasado por referencia (no se permite un valor constante)");
                }
                
                // Es un factor correcto
                tipoResultado=new Entero();
                MepaH.generarInstruccion("APCT "+ ultimoToken.getLexema());
                ultimoToken = alex.getToken();
            }
            else{
                throw new ExcepcionErrorSintactico("Expresion incorrecta, alrededor de '"+ultimoToken.getLexema() +"..' en linea: " + ultimoToken.getLinea());
            }
        }

        return tipoResultado;
    }


    @SuppressWarnings("empty-statement")
    private Tipo restoFactor(Elemento idObj, String lexema, int pasaje)throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico {
        Tipo tipoResultado = null;

        if (ultimoToken.isToken(Token.CORABRE)){
            ultimoToken = alex.getToken();
            tipoResultado = restoFactorArreglo(idObj, lexema, pasaje);

            if (ultimoToken.isToken(Token.CORCIERRA)){
                ultimoToken = alex.getToken();}
            else {
                throw new ExcepcionErrorSintactico("Se esperaba ']' en la linea " + ultimoToken.getLinea());
            }
        }
        else if (ultimoToken.isToken(Token.PARABRE)){
            // Debe ser una llamada a funcion
            if (idObj.esFuncion()){
                if (((Funcion)idObj).getCantidadParametros()==0){
                    throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". La función no puede ser invocada con parámetros");
                }
                
            }

            tipoResultado = restoFactorFuncion(idObj, lexema);
        }
        else{
           tipoResultado = restoFactorIdentificador(idObj, lexema,pasaje);
           
        }

        return tipoResultado;
    }


    private Tipo restoFactorArreglo(Elemento idObj, String lexema, int pasaje)throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico {
       
        Tipo tipoExpresion = expresion(VALOR); //(pasaje);
        Tipo tipoResultado = null;
  
        tipoSimple(tipoExpresion);


        if (pasaje == REFERENCIA){
            if (idObj.esVariable()){
                Variable idObjPAR = ((Variable)idObj);
                if(idObjPAR.getTipo().esArreglo()){
                    if (TdS.compatibles(((Arreglo)((Variable)idObj).getTipo()).getTipoIndice(), tipoExpresion)){

                        // Controlo que la expresion a ser utilizada como indice del arreglo sea de tipo simple
                        tipoSimple(tipoExpresion);
                        
                        //if ((tipoExpresion.esEntero()) || tipoExpresion.esSubrango() || tipoExpresion.esBoolean()){
                            // testear si está dentro del rango
                            Arreglo ArrIdObj = (Arreglo)((Variable)idObj).getTipo();
                            MepaH.generarInstruccion("CONT "+ ArrIdObj.getli() +" , "+ ArrIdObj.getls());

                            //normalizo los indices
                            MepaH.generarInstruccion("APCT "+ ArrIdObj.getli());
                            MepaH.generarInstruccion("SUST");

                            //apilo el valor del arreglo
                            MepaH.generarInstruccion("APDC " + ((Variable)idObj).getNivelLexico()+" , "+ ((Variable)idObj).getOffset());
                            tipoResultado = ArrIdObj.getTipoElemento();
                        //}
                    }
                    else{
                        throw new ExcepcionErrorSemantico(("En linea: " + ultimoToken.getLinea() + ". El tipo de la expresion no es compatible con el tipo indice del arreglo"));
                    }
                }
                else{
                    throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se intenta indexar a un identificador que no corresponde a una variable de tipo arreglo");
                }
            }
            else if (idObj.esParametro()){
                ParametroFormal idObjPAR = (ParametroFormal) idObj;

                if (idObjPAR.getTipo().esArreglo()){
                    if (TdS.compatibles(((Arreglo)idObjPAR.getTipo()).getTipoIndice(), tipoExpresion)){
                        
                        // Controlo que la expresion a ser utilizada como indice del arreglo sea de tipo simple
                        tipoSimple(tipoExpresion);

                        //if ((tipoExpresion.esEntero() || tipoExpresion.esSubrango()) || tipoExpresion.esBoolean()){
                            // testear si está dentro del rango
                            MepaH.generarInstruccion ("CONT " + ((Arreglo)idObjPAR.getTipo()).getli()+" , "+ ((Arreglo)idObjPAR.getTipo()).getls());

                            //normalizo los indices
                            MepaH.generarInstruccion("APCT "+ ((Arreglo)idObjPAR.getTipo()).getli());
                            MepaH.generarInstruccion("SUST");


                            if (idObjPAR.getPasaje() == VALOR){
                                //apilo el valor del arreglo
                                MepaH.generarInstruccion("APDC " + idObjPAR.getNivelLexico() + " , " + idObjPAR.getOffset());                                
                            }
                            else{

                                //apilo el valor del arreglo
                                MepaH.generarInstruccion("APVL " + idObjPAR.getNivelLexico() + " , " + idObjPAR.getOffset());
                                MepaH.generarInstruccion("SUMA");
                            }

                            tipoResultado = ((Arreglo)idObjPAR.getTipo()).getTipoElemento();
                        //}
                    }
                    else { throw new ExcepcionErrorSemantico(("En linea: " + ultimoToken.getLinea() + ". El tipo de la expresion no es compatible con el tipo indice del arreglo")); }
                }
                else { throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se intenta indexar a un identificador que no corresponde a una variable de tipo arreglo"); }
            }
            else{
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se intenta indexar a un identificador que no corresponde a una variable de tipo arreglo");
            }
        }
        else{
            // PASAJE POR VALOR
            if (idObj.esVariable()){
                if (((Variable)idObj).getTipo().esArreglo()){

                    if (TdS.compatibles(((Arreglo)((Variable)idObj).getTipo()).getTipoIndice(), tipoExpresion)){

                        // Controlo que la expresion a ser utilizada como indice del arreglo sea de tipo simple
                        tipoSimple(tipoExpresion);

                        //if ((tipoExpresion.esEntero()) || tipoExpresion.esSubrango() || tipoExpresion.esBoolean()){
                            Arreglo ArrIdObj = (Arreglo)((Variable)idObj).getTipo();
                            // testear si está dentro del rango
                            MepaH.generarInstruccion("CONT "+ ArrIdObj.getli() +" , "+ ArrIdObj.getls());

                            //normalizo los indices
                            MepaH.generarInstruccion("APCT "+ ArrIdObj.getli());
                            MepaH.generarInstruccion("SUST");
                            
                            //apilo el valor del arreglo
                            MepaH.generarInstruccion("APAR " + ((Variable)idObj).getNivelLexico()+" , "+ ((Variable)idObj).getOffset());
                            tipoResultado = ArrIdObj.getTipoElemento();

                        //}
                    }
                    else{
                        throw new ExcepcionErrorSemantico(("En linea: " + ultimoToken.getLinea() + ". El tipo de la expresion no es compatible con el tipo indice del arreglo"));
                    }
                }
                else{
                    throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se intenta indexar a un identificador que no corresponde a una variable de tipo arreglo");
                }
            }
            else if (idObj.esParametro()){
                ParametroFormal idObjPAR = (ParametroFormal) idObj;
                if (idObjPAR.getTipo().esArreglo()){
                    if (TdS.compatibles(((Arreglo)idObjPAR.getTipo()).getTipoIndice(), tipoExpresion)){

                        // Controlo que la expresion a ser utilizada como indice del arreglo sea de tipo simple
                        tipoSimple(tipoExpresion);

                        //if ((tipoExpresion.esEntero() || tipoExpresion.esSubrango()) || tipoExpresion.esBoolean()){
                            Arreglo ArrIdObj = (Arreglo)((ParametroFormal)idObj).getTipo();

                            // testear si está dentro del rango
                            MepaH.generarInstruccion ("CONT " + ((Arreglo)idObjPAR.getTipo()).getli()+" , "+ ((Arreglo)idObjPAR.getTipo()).getls());

                            //normalizo los indices
                            MepaH.generarInstruccion("APCT "+ ((Arreglo)idObjPAR.getTipo()).getli());
                            MepaH.generarInstruccion("SUST");

                            if (idObjPAR.getPasaje() == VALOR){
                                //apilo el valor del arreglo
                                MepaH.generarInstruccion("APAR " + idObjPAR.getNivelLexico() + " , " + idObjPAR.getOffset());
                            }
                            else{
                                //apilo el valor del arreglo
                                MepaH.generarInstruccion("APAI " + idObjPAR.getNivelLexico() +" , " + idObjPAR.getOffset());
                            }

                            tipoResultado = ArrIdObj.getTipoElemento();
                        //}
                    }
                    else { throw new ExcepcionErrorSemantico(("En linea: " + ultimoToken.getLinea() + ". El tipo de la expresion no es compatible con el tipo indice del arreglo")); }
                }
                else { throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se intenta indexar a un identificador que no corresponde a una variable de tipo arreglo"); }
            }
            else{
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se intenta indexar a un identificador que no corresponde a una variable de tipo arreglo");
            }
            /**/


        }
        //sentenciaArreglo();
        return tipoResultado;
    }


    private Tipo restoFactorFuncion(Elemento idObj, String lexema)throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico {
        Tipo tipoResultado;
        if (!(idObj.esFuncion())){
            throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se esperaba identificador de función");
        }
        else {
            MepaH.generarInstruccion("RMEM 1");
            sentenciaProcedimientoFuncion(idObj);

            MepaH.generarInstruccion ("LLPR "+ "l" + ((Funcion)idObj).getEtiqueta());
            tipoResultado= ((Funcion)idObj).getTipo();
        }

        return tipoResultado;
    }


    private Tipo restoFactorIdentificador(Elemento idObj, String lexema, int pasaje)throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico {

        if(pasaje == VALOR){
            if(idObj.esVariable()){
                if(((Variable) idObj).getTipo().esArreglo()){
                    // se esta referenciando a un arreglo sin indices, por lo que se debe apilar todo el arreglo
                    // Apilo el arreglo
                    MepaH.generarInstruccion("PUAR "+  ((Variable)idObj).getNivelLexico()+" , "+ ((Variable)idObj).getOffset()+" , "+ ((Variable)idObj).getTipo().getCantElem());
                    return ((Variable)idObj).getTipo();
                }
                else{
                    // Es una variable de tipo simple
                    

                    MepaH.generarInstruccion("APVL "+ ((Variable)idObj).getNivelLexico()+" , "+ ((Variable)idObj).getOffset());
                    return ((Variable)idObj).getTipo();
                }
            }
            else if (idObj.esConstante()){
                MepaH.generarInstruccion("APCT "+ ((Constante)idObj).getValor());

                return ((Constante)idObj).getTipo();
            }
            else if(idObj.esFuncion()){
                if (((Funcion) idObj).getCantidadParametros() == 0){
                    // Se llama a asignar offset parametros para que le asigne un offset a la celda de memoria utilizada para retornar el resultado de la funcion
                    ((Funcion) idObj).asignarOffsetParametros(new ArrayList());
                    MepaH.generarInstruccion("RMEM 1");
                    MepaH.generarInstruccion("LLPR "+ "l" + ((Funcion)idObj).getEtiqueta());
                    return  ((Funcion)idObj).getTipo();
                }
                else{
                    throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Cantidad de parametros insuficiente en la llamada a funcion");
                }
            }
            else if (idObj.esParametro()){
                ParametroFormal idObjPAR;

                idObjPAR = (ParametroFormal)idObj;

                if (idObjPAR.getPasaje() == VALOR){
                    if(idObjPAR.getTipo().esArreglo()){
                        // se esta referenciando a un arreglo sin indices, por lo que se debe apilar todo el arreglo
                        // Apilo el arreglo
                        MepaH.generarInstruccion("PUAR " + idObjPAR.getNivelLexico() + " , " + idObjPAR.getOffset() + " , " + ((Arreglo)idObjPAR.getTipo()).getCantElem());
                    }
                    else{
                        // Es un parametro de tipo simple
                        MepaH.generarInstruccion("APVL " + idObjPAR.getNivelLexico() + " , " + idObjPAR.getOffset());
                    }
                    return idObjPAR.getTipo();
                }
                else {
                    // El pasaje del parametro cuyo identificador estamos evaluando es por referencia
                    if (idObjPAR.getTipo().esArreglo()){
                        // se esta referenciando a un arreglo sin indices, por lo que se debe apilar todo el arreglo
                        // Apilo el arreglo
                        MepaH.generarInstruccion("PUAI " + idObjPAR.getNivelLexico() + " , " + idObjPAR.getOffset() + " , " + ((Arreglo)idObjPAR.getTipo()).getCantElem());
                    }
                    else{
                        // Es una variable de tipo simple
                        MepaH.generarInstruccion("APVI " + idObjPAR.getNivelLexico() + " , " + idObjPAR.getOffset());
                    }
                    return idObjPAR.getTipo();
                }
            }
            else{
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Factor incorrecto");
            }


        }
        else{
            // PASAJE POR REFERENCIA
            if (idObj.esVariable()){
                if (((Variable) idObj).getTipo().esArreglo()){
                    // se esta referenciando a un arreglo sin indices, por lo que se debe apilar todo el arreglo
                    // Apilo el arreglo
                    MepaH.generarInstruccion("APDR "+ ((Variable) idObj).getNivelLexico()+" , "+ ((Variable) idObj).getOffset());
                    return ((Variable) idObj).getTipo();
                }
                else{
                    // Variable de tipo simple
                    MepaH.generarInstruccion("APDR "+ ((Variable) idObj).getNivelLexico() +" , "+ ((Variable) idObj).getOffset());
                    return ((Variable) idObj).getTipo();
                }
            }
            else if (idObj.esParametro()){
                ParametroFormal idObjPAR;

                idObjPAR = (ParametroFormal)idObj;

                if (idObjPAR.getPasaje() == VALOR){
                    if(idObjPAR.getTipo().esArreglo()){
                        // se esta referenciando a un arreglo sin indices, por lo que se debe apilar todo el arreglo
                        // Apilo el arreglo
                        MepaH.generarInstruccion("APDR " + idObjPAR.getNivelLexico() + " , " + idObjPAR.getOffset());
                    }
                    else{
                        // Es un parametro de tipo simple
                        MepaH.generarInstruccion("APDR " + idObjPAR.getNivelLexico() + " , " + idObjPAR.getOffset());
                    }
                    return idObjPAR.getTipo();
                }
                else {
                    // El pasaje del parametro cuyo identificador estamos evaluando es por referencia
                    if (idObjPAR.getTipo().esArreglo()){
                        // se esta referenciando a un arreglo sin indices, por lo que se debe apilar todo el arreglo
                        // Apilo el arreglo
                        MepaH.generarInstruccion("APVL " + idObjPAR.getNivelLexico() + " , " + idObjPAR.getOffset());
                    }
                    else{
                        // Es una variable de tipo simple
                        MepaH.generarInstruccion("APVL " + idObjPAR.getNivelLexico() + " , " + idObjPAR.getOffset());
                    }
                    return idObjPAR.getTipo();
                }
            }
            else{
                //debería apilar el valor o no? preguntar al procer
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". El parametro no puede ser pasado por referencia");
            }

        }
//                return new Tipo();
    }


    // ------------------------------- SENTENCIA WHILE --------------------------------------------


    private void BucleRepetir() throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico {
    //reemplazar tipo de pasaje, pero creo que está bien que sea por VALOR
        int pasaje = VALOR;
        if (ultimoToken.isToken(Token.WHILE)){
            ultimoToken = alex.getToken();

            int etiqueta = TdS.nuevaEtiqueta();
            MepaH.generarInstruccion("l" + etiqueta + " NADA");

            Tipo tipoExpresion= expresion(pasaje);
          
            if (!tipoExpresion.esBoolean()){
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se esperaba una expresion de tipo booleano");
            }
            //Sale de sentencia apuntando al primer token que sigue a la sentencia


            if (ultimoToken.isToken(Token.DO)){
                int etiqueta2 = TdS.nuevaEtiqueta();
                MepaH.generarInstruccion("DSVF " + "l" + etiqueta2);
                ultimoToken = alex.getToken();


                if (ultimoToken.isToken(Token.BEGIN)){
                    parteSentencias();
                    // Sale de parte sentencias con ultimoToken apuntando al siguiente token luego de end

                }
                else{
                    sentencia();
                    //Sale de sentencia apuntando al primer token que sigue a la sentencia
                }
                MepaH.generarInstruccion("DSVS " + "l" + etiqueta);
                MepaH.generarInstruccion("l" + etiqueta2+ " NADA");
            } else {throw new ExcepcionErrorSintactico("Se esperaba 'DO' en la linea " + ultimoToken.getLinea());}
        }
    }


    // ------------------------------- SENTENCIA IF  --------------------------------------------


    private void SentenciaCondicional() throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico {

        int pasaje = VALOR;
        if (ultimoToken.isToken(Token.IF)){
            ultimoToken = alex.getToken();
            Tipo tipoExpresion = expresion(pasaje);
            int etiqueta1 = TdS.nuevaEtiqueta();
            if(!tipoExpresion.esBoolean()){
                throw new ExcepcionErrorSemantico("En linea: "+ ultimoToken.getLinea()+". Se esperaba una expresion de tipo booleano");
            }

            MepaH.generarInstruccion("DSVF " + "l" + etiqueta1);

            if (ultimoToken.isToken(Token.THEN)){
                ultimoToken = alex.getToken();


                // Permite sentencias multiples entre un begin y end, o una sola sentencia despues del then
                if (ultimoToken.isToken(Token.BEGIN)){
                    parteSentencias();


                    // Sale de parte sentencias con ultimoToken apuntando al siguiente token luego de end


                    if (ultimoToken.isToken(Token.PUNTOCOMA)){
                        ultimoToken = alex.getToken();
                        MepaH.generarInstruccion("l" + etiqueta1+ " NADA");
                    }else if(ultimoToken.isToken(Token.END)){
                        MepaH.generarInstruccion("l" + etiqueta1+ " NADA");
                    }
                    else{
                        SentenciaCondicionalElse(etiqueta1);
                    }
                }
                else{
                    sentencia();
                    //Sale de sentencia apuntando al primer token que sigue a la sentencia


                    if (ultimoToken.isToken(Token.PUNTOCOMA)){
                        ultimoToken = alex.getToken();
                        MepaH.generarInstruccion("l" + etiqueta1+ " NADA");
                    }else if(ultimoToken.isToken(Token.END)){
                        MepaH.generarInstruccion("l" + etiqueta1+ " NADA");
                    }
                    else{
                        SentenciaCondicionalElse(etiqueta1);
                    }

                }
            } else { throw new ExcepcionErrorSintactico("Se esperaba 'THEN' en la linea " + ultimoToken.getLinea());}
        }
    }




    private void SentenciaCondicionalElse(int etiqueta) throws AleXcore.ExcepcionCaracterInvalido, AleXcore.ExcepcionComentarioSinCerrar, AleXcore.ExcepcionIdentificadorInvalido, AleXcore.ExcepcionComentarioCerrado, ExcepcionErrorSintactico, ExcepcionErrorSemantico {
        if (ultimoToken.isToken(Token.ELSE)){
            ultimoToken = alex.getToken();
            int etiqueta2 = TdS.nuevaEtiqueta();
            MepaH.generarInstruccion("DSVS " + "l" + etiqueta2);
            MepaH.generarInstruccion("l" + etiqueta+ " NADA");

            if (ultimoToken.isToken(Token.BEGIN)){
                parteSentencias();
                // Sale de parte sentencias con ultimoToken apuntando al siguiente token luego de end
            }
            else {
                sentencia();
                //Sale de sentencia apuntando al primer token que sigue a la sentencia
            }
            MepaH.generarInstruccion("l" + etiqueta2+ " NADA");
        }
        else { throw new ExcepcionErrorSintactico("Se esperaba ';' en la linea " + ultimoToken.getLinea()); }
    }







//---------------------------------------------------------------------------------------
    // FUNCIONES PREDEFINIDAS

    private void declaracionFuncionRead(){
        int etiqueta = ((Procedimiento)TdS.getEntrada("read")).getEtiqueta();
        
        MepaH.generarInstruccion("l"+ etiqueta + " ENPR 1");
        MepaH.generarInstruccion("LEER");
        MepaH.generarInstruccion("ALVL 1 , -3");
        MepaH.generarInstruccion("LMEM 0");
        MepaH.generarInstruccion("RTPR 1,1");
        
    }

    private void declaracionFuncionPred(){
        int etiqueta = ((Funcion)TdS.getEntrada("pred")).getEtiqueta();
        MepaH.generarInstruccion("l"+ etiqueta + " ENPR 1");
        MepaH.generarInstruccion("APVL 1 , -3");
        MepaH.generarInstruccion("APCT 1");
        MepaH.generarInstruccion("SUST");
        MepaH.generarInstruccion("ALVL 1 , -4");
        MepaH.generarInstruccion("LMEM 0");
        MepaH.generarInstruccion("RTPR 1,1");
    }

    private void declaracionFuncionSucc(){
        int etiqueta = ((Funcion)TdS.getEntrada("succ")).getEtiqueta();
        MepaH.generarInstruccion("l"+ etiqueta + " ENPR 1");
        MepaH.generarInstruccion("APVL 1 , -3");
        MepaH.generarInstruccion("APCT 1");
        MepaH.generarInstruccion("SUMA");
        MepaH.generarInstruccion("ALVL 1 , -4");
        MepaH.generarInstruccion("LMEM 0");
        MepaH.generarInstruccion("RTPR 1,1");
    }

    private void declararFuncionesPredefinidas(){
        declaracionFuncionPred();
        declaracionFuncionRead();
        declaracionFuncionSucc();
    }
    
    private void registrarFuncionSucc(){
        int etiqueta;
        Funcion f;
        int nivelLexico = 0;
        ArrayList parametro= new ArrayList();
        parametro.add(new ParametroFormal(1, -3, new Entero(), VALOR));
        etiqueta = TdS.nuevaEtiqueta();
        f = new Funcion(etiqueta,parametro , new Entero(), nivelLexico);
        TdS.getTope().registrarFuncion("succ", f);
    }

        private void registrarFuncionPred(){
        int etiqueta;
        Funcion f;
        int nivelLexico = 0;
        ArrayList parametro= new ArrayList();
        parametro.add(new ParametroFormal(1, -3, new Entero(), VALOR));
        etiqueta = TdS.nuevaEtiqueta();
        f = new Funcion(etiqueta,parametro , new Entero(), nivelLexico);
        TdS.getTope().registrarFuncion("pred", f);
    }

     private void registrarFuncionRead(){
        int etiqueta;
        Procedimiento f;
        int nivelLexico = 0;
        ArrayList parametro= new ArrayList();
        parametro.add(new ParametroFormal(1, -3, new Entero(), REFERENCIA));
        etiqueta = TdS.nuevaEtiqueta();
        f = new Procedimiento(etiqueta,parametro , nivelLexico);
        TdS.getTope().registrarProcedimiento("read", f);
    }

    private void registrarFuncionesPredefinidas(){
        registrarFuncionSucc();
        registrarFuncionPred();
        registrarFuncionRead();
    }

}
